cmssamp.c 18.7 KB
Newer Older
D
duke 已提交
1 2 3 4 5
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
6
 * published by the Free Software Foundation.  Oracle designates this
D
duke 已提交
7
 * particular file as subject to the "Classpath" exception as provided
8
 * by Oracle in the LICENSE file that accompanied this code.
D
duke 已提交
9 10 11 12 13 14 15 16 17 18 19
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
20 21 22
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
D
duke 已提交
23 24 25 26 27 28 29
 */

// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
30
//---------------------------------------------------------------------------------
D
duke 已提交
31
//
32
//  Little Color Management System
P
prr 已提交
33
//  Copyright (c) 1998-2017 Marti Maria Saguer
D
duke 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 53 54
//
//---------------------------------------------------------------------------------
//
D
duke 已提交
55

56
#include "lcms2_internal.h"
D
duke 已提交
57 58


P
prr 已提交
59 60
#define cmsmin(a, b) (((a) < (b)) ? (a) : (b))
#define cmsmax(a, b) (((a) > (b)) ? (a) : (b))
D
duke 已提交
61

62 63
// This file contains routines for resampling and LUT optimization, black point detection
// and black preservation.
D
duke 已提交
64

65
// Black point detection -------------------------------------------------------------------------
D
duke 已提交
66 67


68
// PCS -> PCS round trip transform, always uses relative intent on the device -> pcs
D
duke 已提交
69
static
70
cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent)
D
duke 已提交
71
{
P
prr 已提交
72 73
    cmsContext ContextID = cmsGetProfileContextID(hProfile);
    cmsHPROFILE hLab = cmsCreateLab4ProfileTHR(ContextID, NULL);
74 75 76 77 78 79 80 81 82 83 84 85 86 87
    cmsHTRANSFORM xform;
    cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE };
    cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 };
    cmsHPROFILE hProfiles[4];
    cmsUInt32Number Intents[4];

    hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab;
    Intents[0]   = INTENT_RELATIVE_COLORIMETRIC; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = INTENT_RELATIVE_COLORIMETRIC;

    xform =  cmsCreateExtendedTransform(ContextID, 4, hProfiles, BPC, Intents,
        States, NULL, 0, TYPE_Lab_DBL, TYPE_Lab_DBL, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE);

    cmsCloseProfile(hLab);
    return xform;
D
duke 已提交
88 89
}

90 91
// Use darker colorants to obtain black point. This works in the relative colorimetric intent and
// assumes more ink results in darker colors. No ink limit is assumed.
D
duke 已提交
92
static
93 94 95 96
cmsBool  BlackPointAsDarkerColorant(cmsHPROFILE    hInput,
                                    cmsUInt32Number Intent,
                                    cmsCIEXYZ* BlackPoint,
                                    cmsUInt32Number dwFlags)
D
duke 已提交
97
{
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    cmsUInt16Number *Black;
    cmsHTRANSFORM xform;
    cmsColorSpaceSignature Space;
    cmsUInt32Number nChannels;
    cmsUInt32Number dwFormat;
    cmsHPROFILE hLab;
    cmsCIELab  Lab;
    cmsCIEXYZ  BlackXYZ;
    cmsContext ContextID = cmsGetProfileContextID(hInput);

    // If the profile does not support input direction, assume Black point 0
    if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) {

        BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
        return FALSE;
D
duke 已提交
113 114
    }

115 116
    // Create a formatter which has n channels and floating point
    dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2, FALSE);
D
duke 已提交
117

118 119
   // Try to get black by using black colorant
    Space = cmsGetColorSpace(hInput);
D
duke 已提交
120

121 122
    // This function returns darker colorant in 16 bits for several spaces
    if (!_cmsEndPointsBySpace(Space, NULL, &Black, &nChannels)) {
D
duke 已提交
123

124 125
        BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
        return FALSE;
D
duke 已提交
126 127
    }

128 129 130
    if (nChannels != T_CHANNELS(dwFormat)) {
       BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
       return FALSE;
D
duke 已提交
131 132
    }

133 134 135 136 137 138
    // Lab will be used as the output space, but lab2 will avoid recursion
    hLab = cmsCreateLab2ProfileTHR(ContextID, NULL);
    if (hLab == NULL) {
       BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
       return FALSE;
    }
D
duke 已提交
139

140 141 142 143
    // Create the transform
    xform = cmsCreateTransformTHR(ContextID, hInput, dwFormat,
                                hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
    cmsCloseProfile(hLab);
D
duke 已提交
144

145
    if (xform == NULL) {
D
duke 已提交
146

P
prr 已提交
147
        // Something went wrong. Get rid of open resources and return zero as black
148 149
        BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
        return FALSE;
D
duke 已提交
150 151
    }

152 153
    // Convert black to Lab
    cmsDoTransform(xform, Black, &Lab, 1);
D
duke 已提交
154

155 156 157
    // Force it to be neutral, clip to max. L* of 50
    Lab.a = Lab.b = 0;
    if (Lab.L > 50) Lab.L = 50;
D
duke 已提交
158

159 160
    // Free the resources
    cmsDeleteTransform(xform);
D
duke 已提交
161

162 163
    // Convert from Lab (which is now clipped) to XYZ.
    cmsLab2XYZ(NULL, &BlackXYZ, &Lab);
D
duke 已提交
164

165 166
    if (BlackPoint != NULL)
        *BlackPoint = BlackXYZ;
D
duke 已提交
167

168
    return TRUE;
D
duke 已提交
169

170
    cmsUNUSED_PARAMETER(dwFlags);
D
duke 已提交
171 172
}

173 174 175 176 177
// Get a black point of output CMYK profile, discounting any ink-limiting embedded
// in the profile. For doing that, we use perceptual intent in input direction:
// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab
static
cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile)
D
duke 已提交
178
{
179 180 181
    cmsHTRANSFORM hRoundTrip;
    cmsCIELab LabIn, LabOut;
    cmsCIEXYZ  BlackXYZ;
D
duke 已提交
182

183 184
     // Is the intent supported by the profile?
    if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) {
185

186 187 188
        BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
        return TRUE;
    }
D
duke 已提交
189

190 191 192 193 194
    hRoundTrip = CreateRoundtripXForm(hProfile, INTENT_PERCEPTUAL);
    if (hRoundTrip == NULL) {
        BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
        return FALSE;
    }
D
duke 已提交
195

196 197
    LabIn.L = LabIn.a = LabIn.b = 0;
    cmsDoTransform(hRoundTrip, &LabIn, &LabOut, 1);
D
duke 已提交
198

199 200 201
    // Clip Lab to reasonable limits
    if (LabOut.L > 50) LabOut.L = 50;
    LabOut.a = LabOut.b = 0;
D
duke 已提交
202

203
    cmsDeleteTransform(hRoundTrip);
D
duke 已提交
204

205 206
    // Convert it to XYZ
    cmsLab2XYZ(NULL, &BlackXYZ, &LabOut);
D
duke 已提交
207

208 209
    if (BlackPoint != NULL)
        *BlackPoint = BlackXYZ;
D
duke 已提交
210

211
    return TRUE;
D
duke 已提交
212 213
}

214 215 216 217 218 219 220
// This function shouldn't exist at all -- there is such quantity of broken
// profiles on black point tag, that we must somehow fix chromaticity to
// avoid huge tint when doing Black point compensation. This function does
// just that. There is a special flag for using black point tag, but turned
// off by default because it is bogus on most profiles. The detection algorithm
// involves to turn BP to neutral and to use only L component.
cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
D
duke 已提交
221
{
P
prr 已提交
222 223 224 225 226 227 228 229 230 231
    cmsProfileClassSignature devClass;

    // Make sure the device class is adequate
    devClass = cmsGetDeviceClass(hProfile);
    if (devClass == cmsSigLinkClass ||
        devClass == cmsSigAbstractClass ||
        devClass == cmsSigNamedColorClass) {
            BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
            return FALSE;
    }
D
duke 已提交
232

P
prr 已提交
233 234 235 236 237 238
    // Make sure intent is adequate
    if (Intent != INTENT_PERCEPTUAL &&
        Intent != INTENT_RELATIVE_COLORIMETRIC &&
        Intent != INTENT_SATURATION) {
            BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
            return FALSE;
D
duke 已提交
239 240
    }

241 242 243 244
    // v4 + perceptual & saturation intents does have its own black point, and it is
    // well specified enough to use it. Black point tag is deprecated in V4.
    if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) &&
        (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) {
D
duke 已提交
245

246 247 248
            // Matrix shaper share MRC & perceptual intents
            if (cmsIsMatrixShaper(hProfile))
                return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0);
D
duke 已提交
249

250 251 252 253
            // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents
            BlackPoint -> X = cmsPERCEPTUAL_BLACK_X;
            BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y;
            BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z;
D
duke 已提交
254

255
            return TRUE;
D
duke 已提交
256 257 258
    }


259
#ifdef CMS_USE_PROFILE_BLACK_POINT_TAG
D
duke 已提交
260

261 262 263
    // v2, v4 rel/abs colorimetric
    if (cmsIsTag(hProfile, cmsSigMediaBlackPointTag) &&
        Intent == INTENT_RELATIVE_COLORIMETRIC) {
D
duke 已提交
264

265 266
            cmsCIEXYZ *BlackPtr, BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite;
            cmsCIELab Lab;
D
duke 已提交
267

268
            // If black point is specified, then use it,
D
duke 已提交
269

270 271
            BlackPtr = cmsReadTag(hProfile, cmsSigMediaBlackPointTag);
            if (BlackPtr != NULL) {
D
duke 已提交
272

273 274
                BlackXYZ = *BlackPtr;
                _cmsReadMediaWhitePoint(&MediaWhite, hProfile);
D
duke 已提交
275

276 277
                // Black point is absolute XYZ, so adapt to D50 to get PCS value
                cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ);
D
duke 已提交
278

279 280 281 282 283
                // Force a=b=0 to get rid of any chroma
                cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint);
                Lab.a = Lab.b = 0;
                if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50
                cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab);
D
duke 已提交
284

285 286
                if (BlackPoint != NULL)
                    *BlackPoint = TrustedBlackPoint;
D
duke 已提交
287

288 289
                return TRUE;
            }
D
duke 已提交
290 291 292
    }
#endif

293
    // That is about v2 profiles.
D
duke 已提交
294

295 296 297 298 299
    // If output profile, discount ink-limiting and that's all
    if (Intent == INTENT_RELATIVE_COLORIMETRIC &&
        (cmsGetDeviceClass(hProfile) == cmsSigOutputClass) &&
        (cmsGetColorSpace(hProfile)  == cmsSigCmykData))
        return BlackPointUsingPerceptualBlack(BlackPoint, hProfile);
D
duke 已提交
300

301 302
    // Nope, compute BP using current intent.
    return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags);
D
duke 已提交
303
}
304

B
bae 已提交
305 306 307 308 309 310 311 312 313 314 315 316


// ---------------------------------------------------------------------------------------------------------

// Least Squares Fit of a Quadratic Curve to Data
// http://www.personal.psu.edu/jhm/f90/lectures/lsq2.html

static
cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[], cmsFloat64Number y[])
{
    double sum_x = 0, sum_x2 = 0, sum_x3 = 0, sum_x4 = 0;
    double sum_y = 0, sum_yx = 0, sum_yx2 = 0;
P
prr 已提交
317
    double d, a, b, c;
B
bae 已提交
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
    int i;
    cmsMAT3 m;
    cmsVEC3 v, res;

    if (n < 4) return 0;

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

        double xn = x[i];
        double yn = y[i];

        sum_x  += xn;
        sum_x2 += xn*xn;
        sum_x3 += xn*xn*xn;
        sum_x4 += xn*xn*xn*xn;

        sum_y += yn;
        sum_yx += yn*xn;
        sum_yx2 += yn*xn*xn;
    }

    _cmsVEC3init(&m.v[0], n,      sum_x,  sum_x2);
    _cmsVEC3init(&m.v[1], sum_x,  sum_x2, sum_x3);
    _cmsVEC3init(&m.v[2], sum_x2, sum_x3, sum_x4);

    _cmsVEC3init(&v, sum_y, sum_yx, sum_yx2);

    if (!_cmsMAT3solve(&res, &m, &v)) return 0;


P
prr 已提交
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
    a = res.n[2];
    b = res.n[1];
    c = res.n[0];

    if (fabs(a) < 1.0E-10) {

        return cmsmin(0, cmsmax(50, -c/b ));
    }
    else {

         d = b*b - 4.0 * a * c;
         if (d <= 0) {
             return 0;
         }
         else {

             double rt = (-b + sqrt(d)) / (2.0 * a);

             return cmsmax(0, cmsmin(50, rt));
         }
   }

B
bae 已提交
370 371 372 373 374 375 376 377 378 379 380
}



// Calculates the black point of a destination profile.
// This algorithm comes from the Adobe paper disclosing its black point compensation method.
cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
{
    cmsColorSpaceSignature ColorSpace;
    cmsHTRANSFORM hRoundTrip = NULL;
    cmsCIELab InitialLab, destLab, Lab;
P
prr 已提交
381
    cmsFloat64Number inRamp[256], outRamp[256];
B
bae 已提交
382
    cmsFloat64Number MinL, MaxL;
P
prr 已提交
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
    cmsBool NearlyStraightMidrange = TRUE;
    cmsFloat64Number yRamp[256];
    cmsFloat64Number x[256], y[256];
    cmsFloat64Number lo, hi;
    int n, l;
    cmsProfileClassSignature devClass;

    // Make sure the device class is adequate
    devClass = cmsGetDeviceClass(hProfile);
    if (devClass == cmsSigLinkClass ||
        devClass == cmsSigAbstractClass ||
        devClass == cmsSigNamedColorClass) {
            BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
            return FALSE;
    }
B
bae 已提交
398 399 400 401 402

    // Make sure intent is adequate
    if (Intent != INTENT_PERCEPTUAL &&
        Intent != INTENT_RELATIVE_COLORIMETRIC &&
        Intent != INTENT_SATURATION) {
P
prr 已提交
403 404
            BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
            return FALSE;
B
bae 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
    }


    // v4 + perceptual & saturation intents does have its own black point, and it is
    // well specified enough to use it. Black point tag is deprecated in V4.
    if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) &&
        (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) {

            // Matrix shaper share MRC & perceptual intents
            if (cmsIsMatrixShaper(hProfile))
                return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0);

            // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents
            BlackPoint -> X = cmsPERCEPTUAL_BLACK_X;
            BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y;
            BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z;
            return TRUE;
    }


    // Check if the profile is lut based and gray, rgb or cmyk (7.2 in Adobe's document)
    ColorSpace = cmsGetColorSpace(hProfile);
    if (!cmsIsCLUT(hProfile, Intent, LCMS_USED_AS_OUTPUT ) ||
        (ColorSpace != cmsSigGrayData &&
         ColorSpace != cmsSigRgbData  &&
         ColorSpace != cmsSigCmykData)) {

        // In this case, handle as input case
        return cmsDetectBlackPoint(BlackPoint, hProfile, Intent, dwFlags);
    }

P
prr 已提交
436
    // It is one of the valid cases!, use Adobe algorithm
B
bae 已提交
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467


    // Set a first guess, that should work on good profiles.
    if (Intent == INTENT_RELATIVE_COLORIMETRIC) {

        cmsCIEXYZ IniXYZ;

        // calculate initial Lab as source black point
        if (!cmsDetectBlackPoint(&IniXYZ, hProfile, Intent, dwFlags)) {
            return FALSE;
        }

        // convert the XYZ to lab
        cmsXYZ2Lab(NULL, &InitialLab, &IniXYZ);

    } else {

        // set the initial Lab to zero, that should be the black point for perceptual and saturation
        InitialLab.L = 0;
        InitialLab.a = 0;
        InitialLab.b = 0;
    }


    // Step 2
    // ======

    // Create a roundtrip. Define a Transform BT for all x in L*a*b*
    hRoundTrip = CreateRoundtripXForm(hProfile, Intent);
    if (hRoundTrip == NULL)  return FALSE;

P
prr 已提交
468
    // Compute ramps
B
bae 已提交
469

P
prr 已提交
470
    for (l=0; l < 256; l++) {
B
bae 已提交
471

P
prr 已提交
472 473 474
        Lab.L = (cmsFloat64Number) (l * 100.0) / 255.0;
        Lab.a = cmsmin(50, cmsmax(-50, InitialLab.a));
        Lab.b = cmsmin(50, cmsmax(-50, InitialLab.b));
B
bae 已提交
475

P
prr 已提交
476
        cmsDoTransform(hRoundTrip, &Lab, &destLab, 1);
B
bae 已提交
477

P
prr 已提交
478 479 480
        inRamp[l]  = Lab.L;
        outRamp[l] = destLab.L;
    }
B
bae 已提交
481

P
prr 已提交
482 483 484 485
    // Make monotonic
    for (l = 254; l > 0; --l) {
        outRamp[l] = cmsmin(outRamp[l], outRamp[l+1]);
    }
B
bae 已提交
486

P
prr 已提交
487 488 489 490 491 492 493
    // Check
    if (! (outRamp[0] < outRamp[255])) {

        cmsDeleteTransform(hRoundTrip);
        BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
        return FALSE;
    }
B
bae 已提交
494 495


P
prr 已提交
496 497 498 499
    // Test for mid range straight (only on relative colorimetric)
    NearlyStraightMidrange = TRUE;
    MinL = outRamp[0]; MaxL = outRamp[255];
    if (Intent == INTENT_RELATIVE_COLORIMETRIC) {
B
bae 已提交
500

P
prr 已提交
501
        for (l=0; l < 256; l++) {
B
bae 已提交
502

P
prr 已提交
503 504 505
            if (! ((inRamp[l] <= MinL + 0.2 * (MaxL - MinL) ) ||
                (fabs(inRamp[l] - outRamp[l]) < 4.0 )))
                NearlyStraightMidrange = FALSE;
B
bae 已提交
506 507
        }

P
prr 已提交
508 509 510 511 512
        // If the mid range is straight (as determined above) then the
        // DestinationBlackPoint shall be the same as initialLab.
        // Otherwise, the DestinationBlackPoint shall be determined
        // using curve fitting.
        if (NearlyStraightMidrange) {
B
bae 已提交
513

P
prr 已提交
514 515 516 517
            cmsLab2XYZ(NULL, BlackPoint, &InitialLab);
            cmsDeleteTransform(hRoundTrip);
            return TRUE;
        }
B
bae 已提交
518 519
    }

P
prr 已提交
520 521

    // curve fitting: The round-trip curve normally looks like a nearly constant section at the black point,
B
bae 已提交
522
    // with a corner and a nearly straight line to the white point.
P
prr 已提交
523 524 525 526
    for (l=0; l < 256; l++) {

        yRamp[l] = (outRamp[l] - MinL) / (MaxL - MinL);
    }
B
bae 已提交
527 528 529 530 531 532 533 534 535 536 537 538 539

    // find the black point using the least squares error quadratic curve fitting
    if (Intent == INTENT_RELATIVE_COLORIMETRIC) {
        lo = 0.1;
        hi = 0.5;
    }
    else {

        // Perceptual and saturation
        lo = 0.03;
        hi = 0.25;
    }

P
prr 已提交
540
    // Capture shadow points for the fitting.
B
bae 已提交
541
    n = 0;
P
prr 已提交
542
    for (l=0; l < 256; l++) {
B
bae 已提交
543

P
prr 已提交
544
        cmsFloat64Number ff = yRamp[l];
B
bae 已提交
545 546

        if (ff >= lo && ff < hi) {
P
prr 已提交
547 548
            x[n] = inRamp[l];
            y[n] = yRamp[l];
B
bae 已提交
549 550 551 552 553
            n++;
        }
    }


P
prr 已提交
554 555
    // No suitable points
    if (n < 3 ) {
B
bae 已提交
556
        cmsDeleteTransform(hRoundTrip);
P
prr 已提交
557 558
        BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
        return FALSE;
B
bae 已提交
559 560 561 562 563 564
    }


    // fit and get the vertex of quadratic curve
    Lab.L = RootOfLeastSquaresFitQuadraticCurve(n, x, y);

P
prr 已提交
565
    if (Lab.L < 0.0) { // clip to zero L* if the vertex is negative
B
bae 已提交
566 567 568 569 570 571 572 573 574 575 576
        Lab.L = 0;
    }

    Lab.a = InitialLab.a;
    Lab.b = InitialLab.b;

    cmsLab2XYZ(NULL, BlackPoint, &Lab);

    cmsDeleteTransform(hRoundTrip);
    return TRUE;
}