jdk

Форк
0
1612 строк · 47.2 Кб
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-2023 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
// PostScript ColorRenderingDictionary and ColorSpaceArray
59

60

61
#define MAXPSCOLS   60      // Columns on tables
62

63
/*
64
    Implementation
65
    --------------
66

67
  PostScript does use XYZ as its internal PCS. But since PostScript
68
  interpolation tables are limited to 8 bits, I use Lab as a way to
69
  improve the accuracy, favoring perceptual results. So, for the creation
70
  of each CRD, CSA the profiles are converted to Lab via a device
71
  link between  profile -> Lab or Lab -> profile. The PS code necessary to
72
  convert Lab <-> XYZ is also included.
73

74

75

76
  Color Space Arrays (CSA)
77
  ==================================================================================
78

79
  In order to obtain precision, code chooses between three ways to implement
80
  the device -> XYZ transform. These cases identifies monochrome profiles (often
81
  implemented as a set of curves), matrix-shaper and Pipeline-based.
82

83
  Monochrome
84
  -----------
85

86
  This is implemented as /CIEBasedA CSA. The prelinearization curve is
87
  placed into /DecodeA section, and matrix equals to D50. Since here is
88
  no interpolation tables, I do the conversion directly to XYZ
89

90
  NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT
91
  flag is forced on such profiles.
92

93
    [ /CIEBasedA
94
      <<
95
            /DecodeA { transfer function } bind
96
            /MatrixA [D50]
97
            /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]
98
            /WhitePoint [D50]
99
            /BlackPoint [BP]
100
            /RenderingIntent (intent)
101
      >>
102
    ]
103

104
   On simpler profiles, the PCS is already XYZ, so no conversion is required.
105

106

107
   Matrix-shaper based
108
   -------------------
109

110
   This is implemented both with /CIEBasedABC or /CIEBasedDEF depending on the
111
   profile implementation. Since here there are no interpolation tables, I do
112
   the conversion directly to XYZ
113

114

115

116
    [ /CIEBasedABC
117
            <<
118
                /DecodeABC [ {transfer1} {transfer2} {transfer3} ]
119
                /MatrixABC [Matrix]
120
                /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ]
121
                /DecodeLMN [ { / 2} dup dup ]
122
                /WhitePoint [D50]
123
                /BlackPoint [BP]
124
                /RenderingIntent (intent)
125
            >>
126
    ]
127

128

129
    CLUT based
130
    ----------
131

132
     Lab is used in such cases.
133

134
    [ /CIEBasedDEF
135
            <<
136
            /DecodeDEF [ <prelinearization> ]
137
            /Table [ p p p [<...>]]
138
            /RangeABC [ 0 1 0 1 0 1]
139
            /DecodeABC[ <postlinearization> ]
140
            /RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]
141
               % -128/500 1+127/500 0 1  -127/200 1+128/200
142
            /MatrixABC [ 1 1 1 1 0 0 0 0 -1]
143
            /WhitePoint [D50]
144
            /BlackPoint [BP]
145
            /RenderingIntent (intent)
146
    ]
147

148

149
  Color Rendering Dictionaries (CRD)
150
  ==================================
151
  These are always implemented as CLUT, and always are using Lab. Since CRD are expected to
152
  be used as resources, the code adds the definition as well.
153

154
  <<
155
    /ColorRenderingType 1
156
    /WhitePoint [ D50 ]
157
    /BlackPoint [BP]
158
    /MatrixPQR [ Bradford ]
159
    /RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ]
160
    /TransformPQR [
161
    {4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind
162
    {4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind
163
    {4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind
164
    ]
165
    /MatrixABC <...>
166
    /EncodeABC <...>
167
    /RangeABC  <.. used for  XYZ -> Lab>
168
    /EncodeLMN
169
    /RenderTable [ p p p [<...>]]
170

171
    /RenderingIntent (Perceptual)
172
  >>
173
  /Current exch /ColorRendering defineresource pop
174

175

176
  The following stages are used to convert from XYZ to Lab
177
  --------------------------------------------------------
178

179
  Input is given at LMN stage on X, Y, Z
180

181
  Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn)
182

183
  /EncodeLMN [
184

185
    { 0.964200  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
186
    { 1.000000  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
187
    { 0.824900  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
188

189
    ]
190

191

192
  MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn)
193

194
  | 0  1  0|
195
  | 1 -1  0|
196
  | 0  1 -1|
197

198
  /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]
199

200
 EncodeABC finally gives Lab values.
201

202
  /EncodeABC [
203
    { 116 mul  16 sub 100 div  } bind
204
    { 500 mul 128 add 255 div  } bind
205
    { 200 mul 128 add 255 div  } bind
206
    ]
207

208
  The following stages are used to convert Lab to XYZ
209
  ----------------------------------------------------
210

211
    /RangeABC [ 0 1 0 1 0 1]
212
    /DecodeABC [ { 100 mul 16 add 116 div } bind
213
                 { 255 mul 128 sub 500 div } bind
214
                 { 255 mul 128 sub 200 div } bind
215
               ]
216

217
    /MatrixABC [ 1 1 1 1 0 0 0 0 -1]
218
    /DecodeLMN [
219
                {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind
220
                {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind
221
                {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind
222
                ]
223

224

225
*/
226

227
/*
228

229
 PostScript algorithms discussion.
230
 =========================================================================================================
231

232
  1D interpolation algorithm
233

234

235
  1D interpolation (float)
236
  ------------------------
237

238
    val2 = Domain * Value;
239

240
    cell0 = (int) floor(val2);
241
    cell1 = (int) ceil(val2);
242

243
    rest = val2 - cell0;
244

245
    y0 = LutTable[cell0] ;
246
    y1 = LutTable[cell1] ;
247

248
    y = y0 + (y1 - y0) * rest;
249

250

251

252
  PostScript code                   Stack
253
  ================================================
254

255
  {                                 % v
256
    <check 0..1.0>
257
    [array]                         % v tab
258
    dup                             % v tab tab
259
    length 1 sub                    % v tab dom
260

261
    3 -1 roll                       % tab dom v
262

263
    mul                             % tab val2
264
    dup                             % tab val2 val2
265
    dup                             % tab val2 val2 val2
266
    floor cvi                       % tab val2 val2 cell0
267
    exch                            % tab val2 cell0 val2
268
    ceiling cvi                     % tab val2 cell0 cell1
269

270
    3 index                         % tab val2 cell0 cell1 tab
271
    exch                            % tab val2 cell0 tab cell1
272
    get                             % tab val2 cell0 y1
273

274
    4 -1 roll                       % val2 cell0 y1 tab
275
    3 -1 roll                       % val2 y1 tab cell0
276
    get                             % val2 y1 y0
277

278
    dup                             % val2 y1 y0 y0
279
    3 1 roll                        % val2 y0 y1 y0
280

281
    sub                             % val2 y0 (y1-y0)
282
    3 -1 roll                       % y0 (y1-y0) val2
283
    dup                             % y0 (y1-y0) val2 val2
284
    floor cvi                       % y0 (y1-y0) val2 floor(val2)
285
    sub                             % y0 (y1-y0) rest
286
    mul                             % y0 t1
287
    add                             % y
288
    65535 div                       % result
289

290
  } bind
291

292

293
*/
294

295

296
// This struct holds the memory block currently being write
297
typedef struct {
298
    _cmsStageCLutData* Pipeline;
299
    cmsIOHANDLER* m;
300

301
    int FirstComponent;
302
    int SecondComponent;
303

304
    const char* PreMaj;
305
    const char* PostMaj;
306
    const char* PreMin;
307
    const char* PostMin;
308

309
    int  FixWhite;    // Force mapping of pure white
310

311
    cmsColorSpaceSignature  ColorSpace;  // ColorSpace of profile
312

313

314
} cmsPsSamplerCargo;
315

316
static int _cmsPSActualColumn = 0;
317

318

319
// Convert to byte
320
static
321
cmsUInt8Number Word2Byte(cmsUInt16Number w)
322
{
323
    return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5);
324
}
325

326

327
// Write a cooked byte
328
static
329
void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b)
330
{
331
    _cmsIOPrintf(m, "%02x", b);
332
    _cmsPSActualColumn += 2;
333

334
    if (_cmsPSActualColumn > MAXPSCOLS) {
335

336
        _cmsIOPrintf(m, "\n");
337
        _cmsPSActualColumn = 0;
338
    }
339
}
340

341
// ----------------------------------------------------------------- PostScript generation
342

343

344
// Removes offending carriage returns
345

346
static
347
char* RemoveCR(const char* txt)
348
{
349
    static char Buffer[2048];
350
    char* pt;
351

352
    strncpy(Buffer, txt, 2047);
353
    Buffer[2047] = 0;
354
    for (pt = Buffer; *pt; pt++)
355
            if (*pt == '\n' || *pt == '\r') *pt = ' ';
356

357
    return Buffer;
358

359
}
360

361
static
362
void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile)
363
{
364
    time_t timer;
365
    cmsMLU *Description, *Copyright;
366
    char DescASCII[256], CopyrightASCII[256];
367

368
    time(&timer);
369

370
    Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag);
371
    Copyright   = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag);
372

373
    DescASCII[0] = DescASCII[255] = 0;
374
    CopyrightASCII[0] = CopyrightASCII[255] = 0;
375

376
    if (Description != NULL) cmsMLUgetASCII(Description,  cmsNoLanguage, cmsNoCountry, DescASCII,       255);
377
    if (Copyright != NULL)   cmsMLUgetASCII(Copyright,    cmsNoLanguage, cmsNoCountry, CopyrightASCII,  255);
378

379
    _cmsIOPrintf(m, "%%!PS-Adobe-3.0\n");
380
    _cmsIOPrintf(m, "%%\n");
381
    _cmsIOPrintf(m, "%% %s\n", Title);
382
    _cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII));
383
    _cmsIOPrintf(m, "%%         %s\n", RemoveCR(CopyrightASCII));
384
    _cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!!
385
    _cmsIOPrintf(m, "%%\n");
386
    _cmsIOPrintf(m, "%%%%BeginResource\n");
387

388
}
389

390

391
// Emits White & Black point. White point is always D50, Black point is the device
392
// Black point adapted to D50.
393

394
static
395
void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint)
396
{
397

398
    _cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X,
399
                                          BlackPoint -> Y,
400
                                          BlackPoint -> Z);
401

402
    _cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X,
403
                                          cmsD50_XYZ()->Y,
404
                                          cmsD50_XYZ()->Z);
405
}
406

407

408
static
409
void EmitRangeCheck(cmsIOHANDLER* m)
410
{
411
    _cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if "
412
                    "dup 1.0 gt { pop 1.0 } if ");
413

414
}
415

416
// Does write the intent
417

418
static
419
void EmitIntent(cmsIOHANDLER* m, cmsUInt32Number RenderingIntent)
420
{
421
    const char *intent;
422

423
    switch (RenderingIntent) {
424

425
        case INTENT_PERCEPTUAL:            intent = "Perceptual"; break;
426
        case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break;
427
        case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break;
428
        case INTENT_SATURATION:            intent = "Saturation"; break;
429

430
        default: intent = "Undefined"; break;
431
    }
432

433
    _cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent );
434
}
435

436
//
437
//  Convert L* to Y
438
//
439
//      Y = Yn*[ (L* + 16) / 116] ^ 3   if (L*) >= 6 / 29
440
//        = Yn*( L* / 116) / 7.787      if (L*) < 6 / 29
441
//
442

443
// Lab -> XYZ, see the discussion above
444

445
static
446
void EmitLab2XYZ(cmsIOHANDLER* m)
447
{
448
    _cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n");
449
    _cmsIOPrintf(m, "/DecodeABC [\n");
450
    _cmsIOPrintf(m, "{100 mul  16 add 116 div } bind\n");
451
    _cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n");
452
    _cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n");
453
    _cmsIOPrintf(m, "]\n");
454
    _cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n");
455
    _cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n");
456
    _cmsIOPrintf(m, "/DecodeLMN [\n");
457
    _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n");
458
    _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n");
459
    _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n");
460
    _cmsIOPrintf(m, "]\n");
461
}
462

463

464

465
// Outputs a table of words. It does use 16 bits
466

467
static
468
void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
469
{
470
    cmsUInt32Number i;
471
    cmsFloat64Number gamma;
472

473
    /**
474
    * On error, empty tables or lienar assume gamma 1.0
475
    */
476
    if (Table == NULL ||
477
        Table->nEntries <= 0 ||
478
        cmsIsToneCurveLinear(Table)) {
479

480
        _cmsIOPrintf(m, "{ 1 } bind ");
481
        return;
482
    }
483

484

485
    // Check if is really an exponential. If so, emit "exp"
486
    gamma = cmsEstimateGamma(Table, 0.001);
487
     if (gamma > 0) {
488
            _cmsIOPrintf(m, "{ %g exp } bind ", gamma);
489
            return;
490
     }
491

492
    _cmsIOPrintf(m, "{ ");
493

494
    // Bounds check
495
    EmitRangeCheck(m);
496

497
    // Emit intepolation code
498

499
    // PostScript code                      Stack
500
    // ===============                      ========================
501
                                            // v
502
    _cmsIOPrintf(m, " [");
503

504
    for (i=0; i < Table->nEntries; i++) {
505
    if (i % 10 == 0)
506
            _cmsIOPrintf(m, "\n  ");
507
        _cmsIOPrintf(m, "%d ", Table->Table16[i]);
508
    }
509

510
    _cmsIOPrintf(m, "] ");                        // v tab
511

512
    _cmsIOPrintf(m, "dup ");                      // v tab tab
513
    _cmsIOPrintf(m, "length 1 sub ");             // v tab dom
514
    _cmsIOPrintf(m, "3 -1 roll ");                // tab dom v
515
    _cmsIOPrintf(m, "mul ");                      // tab val2
516
    _cmsIOPrintf(m, "dup ");                      // tab val2 val2
517
    _cmsIOPrintf(m, "dup ");                      // tab val2 val2 val2
518
    _cmsIOPrintf(m, "floor cvi ");                // tab val2 val2 cell0
519
    _cmsIOPrintf(m, "exch ");                     // tab val2 cell0 val2
520
    _cmsIOPrintf(m, "ceiling cvi ");              // tab val2 cell0 cell1
521
    _cmsIOPrintf(m, "3 index ");                  // tab val2 cell0 cell1 tab
522
    _cmsIOPrintf(m, "exch ");                     // tab val2 cell0 tab cell1
523
    _cmsIOPrintf(m, "get\n  ");                   // tab val2 cell0 y1
524
    _cmsIOPrintf(m, "4 -1 roll ");                // val2 cell0 y1 tab
525
    _cmsIOPrintf(m, "3 -1 roll ");                // val2 y1 tab cell0
526
    _cmsIOPrintf(m, "get ");                      // val2 y1 y0
527
    _cmsIOPrintf(m, "dup ");                      // val2 y1 y0 y0
528
    _cmsIOPrintf(m, "3 1 roll ");                 // val2 y0 y1 y0
529
    _cmsIOPrintf(m, "sub ");                      // val2 y0 (y1-y0)
530
    _cmsIOPrintf(m, "3 -1 roll ");                // y0 (y1-y0) val2
531
    _cmsIOPrintf(m, "dup ");                      // y0 (y1-y0) val2 val2
532
    _cmsIOPrintf(m, "floor cvi ");                // y0 (y1-y0) val2 floor(val2)
533
    _cmsIOPrintf(m, "sub ");                      // y0 (y1-y0) rest
534
    _cmsIOPrintf(m, "mul ");                      // y0 t1
535
    _cmsIOPrintf(m, "add ");                      // y
536
    _cmsIOPrintf(m, "65535 div\n");               // result
537

538
    _cmsIOPrintf(m, " } bind ");
539
}
540

541

542
// Compare gamma table
543

544
static
545
cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Number nG1, cmsUInt32Number nG2)
546
{
547
    if (nG1 != nG2) return FALSE;
548
    return memcmp(g1, g2, nG1 * sizeof(cmsUInt16Number)) == 0;
549
}
550

551

552
// Does write a set of gamma curves
553

554
static
555
void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[])
556
{
557
    cmsUInt32Number i;
558

559

560
    for( i=0; i < n; i++ )
561
    {
562
        if (g[i] == NULL) return; // Error
563

564
        if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i-1]->nEntries, g[i]->nEntries)) {
565

566
            _cmsIOPrintf(m, "dup ");
567
        }
568
        else {
569
            Emit1Gamma(m, g[i]);
570
        }
571
    }
572

573
}
574

575

576
// Following code dumps a LUT onto memory stream
577

578

579
// This is the sampler. Intended to work in SAMPLER_INSPECT mode,
580
// that is, the callback will be called for each knot with
581
//
582
//          In[]  The grid location coordinates, normalized to 0..ffff
583
//          Out[] The Pipeline values, normalized to 0..ffff
584
//
585
//  Returning a value other than 0 does terminate the sampling process
586
//
587
//  Each row contains Pipeline values for all but first component. So, I
588
//  detect row changing by keeping a copy of last value of first
589
//  component. -1 is used to mark beginning of whole block.
590

591
static
592
int OutputValueSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo)
593
{
594
    cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo;
595
    cmsUInt32Number i;
596

597

598
    if (sc -> FixWhite) {
599

600
        if (In[0] == 0xFFFF) {  // Only in L* = 100, ab = [-8..8]
601

602
            if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&
603
                (In[2] >= 0x7800 && In[2] <= 0x8800)) {
604

605
                cmsUInt16Number* Black;
606
                cmsUInt16Number* White;
607
                cmsUInt32Number nOutputs;
608

609
                if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))
610
                        return 0;
611

612
                for (i=0; i < nOutputs; i++)
613
                        Out[i] = White[i];
614
            }
615

616

617
        }
618
    }
619

620

621
    // Hadle the parenthesis on rows
622

623
    if (In[0] != sc ->FirstComponent) {
624

625
            if (sc ->FirstComponent != -1) {
626

627
                    _cmsIOPrintf(sc ->m, sc ->PostMin);
628
                    sc ->SecondComponent = -1;
629
                    _cmsIOPrintf(sc ->m, sc ->PostMaj);
630
            }
631

632
            // Begin block
633
            _cmsPSActualColumn = 0;
634

635
            _cmsIOPrintf(sc ->m, sc ->PreMaj);
636
            sc ->FirstComponent = In[0];
637
    }
638

639

640
      if (In[1] != sc ->SecondComponent) {
641

642
            if (sc ->SecondComponent != -1) {
643

644
                    _cmsIOPrintf(sc ->m, sc ->PostMin);
645
            }
646

647
            _cmsIOPrintf(sc ->m, sc ->PreMin);
648
            sc ->SecondComponent = In[1];
649
    }
650

651
      // Dump table.
652

653
      for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) {
654

655
          cmsUInt16Number wWordOut = Out[i];
656
          cmsUInt8Number wByteOut;           // Value as byte
657

658

659
          // We always deal with Lab4
660

661
          wByteOut = Word2Byte(wWordOut);
662
          WriteByte(sc -> m, wByteOut);
663
      }
664

665
      return 1;
666
}
667

668
// Writes a Pipeline on memstream. Could be 8 or 16 bits based
669

670
static
671
void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
672
                                               const char* PostMaj,
673
                                               const char* PreMin,
674
                                               const char* PostMin,
675
                                               int FixWhite,
676
                                               cmsColorSpaceSignature ColorSpace)
677
{
678
    cmsUInt32Number i;
679
    cmsPsSamplerCargo sc;
680

681
    sc.FirstComponent = -1;
682
    sc.SecondComponent = -1;
683
    sc.Pipeline = (_cmsStageCLutData *) mpe ->Data;
684
    sc.m   = m;
685
    sc.PreMaj = PreMaj;
686
    sc.PostMaj= PostMaj;
687

688
    sc.PreMin   = PreMin;
689
    sc.PostMin  = PostMin;
690
    sc.FixWhite = FixWhite;
691
    sc.ColorSpace = ColorSpace;
692

693
    if (sc.Pipeline != NULL && sc.Pipeline->Params != NULL) {
694

695
        _cmsIOPrintf(m, "[");
696

697
        for (i = 0; i < sc.Pipeline->Params->nInputs; i++)
698
            _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
699

700
        _cmsIOPrintf(m, " [\n");
701

702
        cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*)&sc, SAMPLER_INSPECT);
703

704
        _cmsIOPrintf(m, PostMin);
705
        _cmsIOPrintf(m, PostMaj);
706
        _cmsIOPrintf(m, "] ");
707
    }
708

709
}
710

711

712
// Dumps CIEBasedA Color Space Array
713

714
static
715
int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
716
{
717

718
    _cmsIOPrintf(m, "[ /CIEBasedA\n");
719
    _cmsIOPrintf(m, "  <<\n");
720

721
    _cmsIOPrintf(m, "/DecodeA ");
722

723
    Emit1Gamma(m, Curve);
724

725
    _cmsIOPrintf(m, " \n");
726

727
    _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
728
    _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
729

730
    EmitWhiteBlackD50(m, BlackPoint);
731
    EmitIntent(m, INTENT_PERCEPTUAL);
732

733
    _cmsIOPrintf(m, ">>\n");
734
    _cmsIOPrintf(m, "]\n");
735

736
    return 1;
737
}
738

739

740
// Dumps CIEBasedABC Color Space Array
741

742
static
743
int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint)
744
{
745
    int i;
746

747
    _cmsIOPrintf(m, "[ /CIEBasedABC\n");
748
    _cmsIOPrintf(m, "<<\n");
749
    _cmsIOPrintf(m, "/DecodeABC [ ");
750

751
    EmitNGamma(m, 3, CurveSet);
752

753
    _cmsIOPrintf(m, "]\n");
754

755
    _cmsIOPrintf(m, "/MatrixABC [ " );
756

757
    for( i=0; i < 3; i++ ) {
758

759
        _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0],
760
                                           Matrix[i + 3*1],
761
                                           Matrix[i + 3*2]);
762
    }
763

764

765
    _cmsIOPrintf(m, "]\n");
766

767
    _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
768

769
    EmitWhiteBlackD50(m, BlackPoint);
770
    EmitIntent(m, INTENT_PERCEPTUAL);
771

772
    _cmsIOPrintf(m, ">>\n");
773
    _cmsIOPrintf(m, "]\n");
774

775

776
    return 1;
777
}
778

779

780
static
781
int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Intent, cmsCIEXYZ* BlackPoint)
782
{
783
    const char* PreMaj;
784
    const char* PostMaj;
785
    const char* PreMin, *PostMin;
786
    cmsStage* mpe;
787

788
    mpe = Pipeline->Elements;
789

790
    switch (cmsStageInputChannels(mpe)) {
791
    case 3:
792
        _cmsIOPrintf(m, "[ /CIEBasedDEF\n");
793
        PreMaj = "<";
794
        PostMaj = ">\n";
795
        PreMin = PostMin = "";
796
        break;
797

798
    case 4:
799
        _cmsIOPrintf(m, "[ /CIEBasedDEFG\n");
800
        PreMaj = "[";
801
        PostMaj = "]\n";
802
        PreMin = "<";
803
        PostMin = ">\n";
804
        break;
805

806
    default:
807
        return 0;
808

809
    }
810

811
    _cmsIOPrintf(m, "<<\n");
812

813
    if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
814

815
        _cmsIOPrintf(m, "/DecodeDEF [ ");
816
        EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
817
        _cmsIOPrintf(m, "]\n");
818

819
        mpe = mpe ->Next;
820
    }
821

822
    if (cmsStageType(mpe) == cmsSigCLutElemType) {
823

824
            _cmsIOPrintf(m, "/Table ");
825
            WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0);
826
            _cmsIOPrintf(m, "]\n");
827
    }
828

829
    EmitLab2XYZ(m);
830
    EmitWhiteBlackD50(m, BlackPoint);
831
    EmitIntent(m, Intent);
832

833
    _cmsIOPrintf(m, "   >>\n");
834
    _cmsIOPrintf(m, "]\n");
835

836
    return 1;
837
}
838

839
// Generates a curve from a gray profile
840

841
static
842
cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent)
843
{
844
    cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
845
    cmsHPROFILE hXYZ  = cmsCreateXYZProfile();
846
    cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE);
847
    int i;
848

849
    if (Out != NULL && xform != NULL) {
850
        for (i=0; i < 256; i++) {
851

852
            cmsUInt8Number Gray = (cmsUInt8Number) i;
853
            cmsCIEXYZ XYZ;
854

855
            cmsDoTransform(xform, &Gray, &XYZ, 1);
856

857
            Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0);
858
        }
859
    }
860

861
    if (xform) cmsDeleteTransform(xform);
862
    if (hXYZ) cmsCloseProfile(hXYZ);
863
    return Out;
864
}
865

866

867

868
// Because PostScript has only 8 bits in /Table, we should use
869
// a more perceptually uniform space... I do choose Lab.
870

871
static
872
int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
873
{
874
    cmsHPROFILE hLab;
875
    cmsHTRANSFORM xform;
876
    cmsUInt32Number nChannels;
877
    cmsUInt32Number InputFormat;
878
    int rc;
879
    cmsHPROFILE Profiles[2];
880
    cmsCIEXYZ BlackPointAdaptedToD50;
881

882
    // Does create a device-link based transform.
883
    // The DeviceLink is next dumped as working CSA.
884

885
    InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
886
    nChannels   = T_CHANNELS(InputFormat);
887

888

889
    cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
890

891
    // Adjust output to Lab4
892
    hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
893

894
    Profiles[0] = hProfile;
895
    Profiles[1] = hLab;
896

897
    xform = cmsCreateMultiprofileTransform(Profiles, 2,  InputFormat, TYPE_Lab_DBL, Intent, 0);
898
    cmsCloseProfile(hLab);
899

900
    if (xform == NULL) {
901

902
        cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab");
903
        return 0;
904
    }
905

906
    // Only 1, 3 and 4 channels are allowed
907

908
    switch (nChannels) {
909

910
    case 1: {
911
            cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent);
912
            EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50);
913
            cmsFreeToneCurve(Gray2Y);
914
            }
915
            break;
916

917
    case 3:
918
    case 4: {
919
            cmsUInt32Number OutFrm = TYPE_Lab_16;
920
            cmsPipeline* DeviceLink;
921
            _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
922

923
            DeviceLink = cmsPipelineDup(v ->Lut);
924
            if (DeviceLink == NULL) return 0;
925

926
            dwFlags |= cmsFLAGS_FORCE_CLUT;
927
            _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags);
928

929
            rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
930
            cmsPipelineFree(DeviceLink);
931
            if (rc == 0) return 0;
932
            }
933
            break;
934

935
    default:
936

937
        cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels are supported for CSA. This profile has %d channels.", nChannels);
938
        return 0;
939
    }
940

941

942
    cmsDeleteTransform(xform);
943

944
    return 1;
945
}
946

947
static
948
cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe)
949
{
950
    _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
951

952
    return Data -> Double;
953
}
954

955

956
// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
957
static
958
int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper)
959
{
960
    cmsColorSpaceSignature ColorSpace;
961
    int rc;
962
    cmsCIEXYZ BlackPointAdaptedToD50;
963

964
    ColorSpace = cmsGetColorSpace(hProfile);
965

966
    cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
967

968
    if (ColorSpace == cmsSigGrayData) {
969

970
        cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper);
971
        rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50);
972

973
    }
974
    else
975
        if (ColorSpace == cmsSigRgbData) {
976

977
            cmsMAT3 Mat;
978
            int i, j;
979

980
            memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat));
981

982
            for (i = 0; i < 3; i++)
983
                for (j = 0; j < 3; j++)
984
                    Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
985

986
            rc = EmitCIEBasedABC(m,  (cmsFloat64Number *) &Mat,
987
                                _cmsStageGetPtrToCurveSet(Shaper),
988
                                 &BlackPointAdaptedToD50);
989
        }
990
        else {
991

992
            cmsSignalError(m->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace.");
993
            return 0;
994
        }
995

996
    return rc;
997
}
998

999

1000

1001
// Creates a PostScript color list from a named profile data.
1002
// This is a HP extension, and it works in Lab instead of XYZ
1003

1004
static
1005
int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent)
1006
{
1007
    cmsHTRANSFORM xform;
1008
    cmsHPROFILE   hLab;
1009
    cmsUInt32Number i, nColors;
1010
    char ColorName[cmsMAX_PATH];
1011
    cmsNAMEDCOLORLIST* NamedColorList;
1012

1013
    hLab  = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1014
    xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
1015
    cmsCloseProfile(hLab);
1016

1017
    if (xform == NULL) return 0;
1018

1019
    NamedColorList = cmsGetNamedColorList(xform);
1020
    if (NamedColorList == NULL) {
1021
        cmsDeleteTransform(xform);
1022
        return 0;
1023
    }
1024

1025
    _cmsIOPrintf(m, "<<\n");
1026
    _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
1027
    _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1028
    _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1029

1030
    nColors   = cmsNamedColorCount(NamedColorList);
1031

1032
    for (i=0; i < nColors; i++) {
1033

1034
        cmsUInt16Number In[1];
1035
        cmsCIELab Lab;
1036

1037
        In[0] = (cmsUInt16Number) i;
1038

1039
        if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1040
                continue;
1041

1042
        cmsDoTransform(xform, In, &Lab, 1);
1043
        _cmsIOPrintf(m, "  (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
1044
    }
1045

1046
    _cmsIOPrintf(m, ">>\n");
1047

1048
    cmsDeleteTransform(xform);
1049
    return 1;
1050
}
1051

1052

1053
// Does create a Color Space Array on XYZ colorspace for PostScript usage
1054
static
1055
cmsUInt32Number GenerateCSA(cmsContext ContextID,
1056
                            cmsHPROFILE hProfile,
1057
                            cmsUInt32Number Intent,
1058
                            cmsUInt32Number dwFlags,
1059
                            cmsIOHANDLER* mem)
1060
{
1061
    cmsUInt32Number dwBytesUsed;
1062
    cmsPipeline* lut = NULL;
1063
    cmsStage* Matrix, *Shaper;
1064

1065

1066
    // Is a named color profile?
1067
    if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1068

1069
        if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error;
1070
    }
1071
    else {
1072

1073

1074
        // Any profile class are allowed (including devicelink), but
1075
        // output (PCS) colorspace must be XYZ or Lab
1076
        cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
1077

1078
        if (ColorSpace != cmsSigXYZData &&
1079
            ColorSpace != cmsSigLabData) {
1080

1081
                cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space");
1082
                goto Error;
1083
        }
1084

1085

1086
        // Read the lut with all necessary conversion stages
1087
        lut = _cmsReadInputLUT(hProfile, Intent);
1088
        if (lut == NULL) goto Error;
1089

1090

1091
        // Tone curves + matrix can be implemented without any LUT
1092
        if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) {
1093

1094
            if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error;
1095

1096
        }
1097
        else {
1098
           // We need a LUT for the rest
1099
           if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error;
1100
        }
1101
    }
1102

1103

1104
    // Done, keep memory usage
1105
    dwBytesUsed = mem ->UsedSpace;
1106

1107
    // Get rid of LUT
1108
    if (lut != NULL) cmsPipelineFree(lut);
1109

1110
    // Finally, return used byte count
1111
    return dwBytesUsed;
1112

1113
Error:
1114
    if (lut != NULL) cmsPipelineFree(lut);
1115
    return 0;
1116
}
1117

1118
// ------------------------------------------------------ Color Rendering Dictionary (CRD)
1119

1120

1121

1122
/*
1123

1124
  Black point compensation plus chromatic adaptation:
1125

1126
  Step 1 - Chromatic adaptation
1127
  =============================
1128

1129
          WPout
1130
    X = ------- PQR
1131
          Wpin
1132

1133
  Step 2 - Black point compensation
1134
  =================================
1135

1136
          (WPout - BPout)*X - WPout*(BPin - BPout)
1137
    out = ---------------------------------------
1138
                        WPout - BPin
1139

1140

1141
  Algorithm discussion
1142
  ====================
1143

1144
  TransformPQR(WPin, BPin, WPout, BPout, PQR)
1145

1146
  Wpin,etc= { Xws Yws Zws Pws Qws Rws }
1147

1148

1149
  Algorithm             Stack 0...n
1150
  ===========================================================
1151
                        PQR BPout WPout BPin WPin
1152
  4 index 3 get         WPin PQR BPout WPout BPin WPin
1153
  div                   (PQR/WPin) BPout WPout BPin WPin
1154
  2 index 3 get         WPout (PQR/WPin) BPout WPout BPin WPin
1155
  mult                  WPout*(PQR/WPin) BPout WPout BPin WPin
1156

1157
  2 index 3 get         WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1158
  2 index 3 get         BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
1159
  sub                   (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
1160
  mult                  (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1161

1162
  2 index 3 get         WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1163
  4 index 3 get         BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1164
  3 index 3 get         BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1165

1166
  sub                   (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1167
  mult                  (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
1168
  sub                   (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1169

1170
  3 index 3 get         BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1171
  3 index 3 get         WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1172
  exch
1173
  sub                   (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
1174
  div
1175

1176
  exch pop
1177
  exch pop
1178
  exch pop
1179
  exch pop
1180

1181
*/
1182

1183

1184
static
1185
void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
1186
{
1187

1188

1189
        if (lIsAbsolute) {
1190

1191
            // For absolute colorimetric intent, encode back to relative
1192
            // and generate a relative Pipeline
1193

1194
            // Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
1195

1196
            cmsCIEXYZ White;
1197

1198
            _cmsReadMediaWhitePoint(&White, hProfile);
1199

1200
            _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
1201
            _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1202

1203
            _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
1204
                      "/TransformPQR [\n"
1205
                      "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1206
                      "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
1207
                      "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
1208
                      White.X, White.Y, White.Z);
1209
            return;
1210
        }
1211

1212

1213
        _cmsIOPrintf(m,"%% Bradford Cone Space\n"
1214
                 "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
1215

1216
        _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
1217

1218

1219
        // No BPC
1220

1221
        if (!DoBPC) {
1222

1223
            _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n"
1224
                      "/TransformPQR [\n"
1225
                      "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
1226
                      "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
1227
                      "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
1228
        } else {
1229

1230
            // BPC
1231

1232
            _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
1233
                      "/TransformPQR [\n");
1234

1235
            _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul "
1236
                    "2 index 3 get 2 index 3 get sub mul "
1237
                    "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
1238
                    "3 index 3 get 3 index 3 get exch sub div "
1239
                    "exch pop exch pop exch pop exch pop } bind\n");
1240

1241
            _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul "
1242
                    "2 index 4 get 2 index 4 get sub mul "
1243
                    "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
1244
                    "3 index 4 get 3 index 4 get exch sub div "
1245
                    "exch pop exch pop exch pop exch pop } bind\n");
1246

1247
            _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul "
1248
                    "2 index 5 get 2 index 5 get sub mul "
1249
                    "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
1250
                    "3 index 5 get 3 index 5 get exch sub div "
1251
                    "exch pop exch pop exch pop exch pop } bind\n]\n");
1252

1253
        }
1254
}
1255

1256

1257
static
1258
void EmitXYZ2Lab(cmsIOHANDLER* m)
1259
{
1260
    _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
1261
    _cmsIOPrintf(m, "/EncodeLMN [\n");
1262
    _cmsIOPrintf(m, "{ 0.964200  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1263
    _cmsIOPrintf(m, "{ 1.000000  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1264
    _cmsIOPrintf(m, "{ 0.824900  div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
1265
    _cmsIOPrintf(m, "]\n");
1266
    _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
1267
    _cmsIOPrintf(m, "/EncodeABC [\n");
1268

1269

1270
    _cmsIOPrintf(m, "{ 116 mul  16 sub 100 div  } bind\n");
1271
    _cmsIOPrintf(m, "{ 500 mul 128 add 256 div  } bind\n");
1272
    _cmsIOPrintf(m, "{ 200 mul 128 add 256 div  } bind\n");
1273

1274

1275
    _cmsIOPrintf(m, "]\n");
1276

1277

1278
}
1279

1280
// Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
1281
// I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
1282
// space on 3D CLUT, but since space seems not to be a problem here, 33 points
1283
// would give a reasonable accuracy. Note also that CRD tables must operate in
1284
// 8 bits.
1285

1286
static
1287
int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
1288
{
1289
    cmsHPROFILE hLab;
1290
    cmsHTRANSFORM xform;
1291
    cmsUInt32Number i, nChannels;
1292
    cmsUInt32Number OutputFormat;
1293
    _cmsTRANSFORM* v;
1294
    cmsPipeline* DeviceLink;
1295
    cmsHPROFILE Profiles[3];
1296
    cmsCIEXYZ BlackPointAdaptedToD50;
1297
    cmsBool lDoBPC = (cmsBool) (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
1298
    cmsBool lFixWhite = (cmsBool) !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
1299
    cmsUInt32Number InFrm = TYPE_Lab_16;
1300
    cmsUInt32Number RelativeEncodingIntent;
1301
    cmsColorSpaceSignature ColorSpace;
1302
    cmsStage* first;
1303

1304
    hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
1305
    if (hLab == NULL) return 0;
1306

1307
    OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE);
1308
    nChannels    = T_CHANNELS(OutputFormat);
1309

1310
    ColorSpace = cmsGetColorSpace(hProfile);
1311

1312
    // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision.
1313

1314
    RelativeEncodingIntent = Intent;
1315
    if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
1316
        RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
1317

1318

1319
    // Use V4 Lab always
1320
    Profiles[0] = hLab;
1321
    Profiles[1] = hProfile;
1322

1323
    xform = cmsCreateMultiprofileTransformTHR(m ->ContextID,
1324
                                              Profiles, 2, TYPE_Lab_DBL,
1325
                                              OutputFormat, RelativeEncodingIntent, 0);
1326
    cmsCloseProfile(hLab);
1327

1328
    if (xform == NULL) {
1329
        cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
1330
        return 0;
1331
    }
1332

1333
    // Get a copy of the internal devicelink
1334
    v = (_cmsTRANSFORM*) xform;
1335
    DeviceLink = cmsPipelineDup(v ->Lut);
1336
    if (DeviceLink == NULL) {
1337
        cmsDeleteTransform(xform);
1338
        return 0;
1339
    }
1340

1341
     // We need a CLUT
1342
    dwFlags |= cmsFLAGS_FORCE_CLUT;
1343
    _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
1344

1345
    _cmsIOPrintf(m, "<<\n");
1346
    _cmsIOPrintf(m, "/ColorRenderingType 1\n");
1347

1348

1349
    cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0);
1350

1351
    // Emit headers, etc.
1352
    EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
1353
    EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
1354
    EmitXYZ2Lab(m);
1355

1356

1357
    // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
1358
    // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
1359
    // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
1360
    // zero. This would sacrifice a bit of highlights, but failure to do so would cause
1361
    // scum dot. Ouch.
1362

1363
    if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1364
            lFixWhite = FALSE;
1365

1366
    _cmsIOPrintf(m, "/RenderTable ");
1367

1368
    first = cmsPipelineGetPtrToFirstStage(DeviceLink);
1369
    if (first != NULL) {
1370
        WriteCLUT(m, first, "<", ">\n", "", "", lFixWhite, ColorSpace);
1371
    }
1372

1373
    _cmsIOPrintf(m, " %d {} bind ", nChannels);
1374

1375
    for (i=1; i < nChannels; i++)
1376
            _cmsIOPrintf(m, "dup ");
1377

1378
    _cmsIOPrintf(m, "]\n");
1379

1380
    EmitIntent(m, Intent);
1381

1382
    _cmsIOPrintf(m, ">>\n");
1383

1384
    if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1385

1386
        _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n");
1387
    }
1388

1389
    cmsPipelineFree(DeviceLink);
1390
    cmsDeleteTransform(xform);
1391

1392
    return 1;
1393
}
1394

1395

1396
// Builds a ASCII string containing colorant list in 0..1.0 range
1397
static
1398
void BuildColorantList(char *Colorant, cmsUInt32Number nColorant, cmsUInt16Number Out[])
1399
{
1400
    char Buff[32];
1401
    cmsUInt32Number j;
1402

1403
    Colorant[0] = 0;
1404
    if (nColorant > cmsMAXCHANNELS)
1405
        nColorant = cmsMAXCHANNELS;
1406

1407
    for (j = 0; j < nColorant; j++) {
1408

1409
        snprintf(Buff, 31, "%.3f", Out[j] / 65535.0);
1410
        Buff[31] = 0;
1411
        strcat(Colorant, Buff);
1412
        if (j < nColorant - 1)
1413
            strcat(Colorant, " ");
1414

1415
    }
1416
}
1417

1418

1419
// Creates a PostScript color list from a named profile data.
1420
// This is a HP extension.
1421

1422
static
1423
int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number Intent, cmsUInt32Number dwFlags)
1424
{
1425
    cmsHTRANSFORM xform;
1426
    cmsUInt32Number i, nColors, nColorant;
1427
    cmsUInt32Number OutputFormat;
1428
    char ColorName[cmsMAX_PATH];
1429
    char Colorant[512];
1430
    cmsNAMEDCOLORLIST* NamedColorList;
1431

1432

1433
    OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE);
1434
    nColorant    = T_CHANNELS(OutputFormat);
1435

1436

1437
    xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags);
1438
    if (xform == NULL) return 0;
1439

1440

1441
    NamedColorList = cmsGetNamedColorList(xform);
1442
    if (NamedColorList == NULL) {
1443
        cmsDeleteTransform(xform);
1444
        return 0;
1445
    }
1446

1447
    _cmsIOPrintf(m, "<<\n");
1448
    _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");
1449
    _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
1450
    _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
1451

1452
    nColors   = cmsNamedColorCount(NamedColorList);
1453

1454
    for (i=0; i < nColors; i++) {
1455

1456
        cmsUInt16Number In[1];
1457
        cmsUInt16Number Out[cmsMAXCHANNELS];
1458

1459
        In[0] = (cmsUInt16Number) i;
1460

1461
        if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL))
1462
                continue;
1463

1464
        cmsDoTransform(xform, In, Out, 1);
1465
        BuildColorantList(Colorant, nColorant, Out);
1466
        _cmsIOPrintf(m, "  (%s) [ %s ]\n", ColorName, Colorant);
1467
    }
1468

1469
    _cmsIOPrintf(m, "   >>");
1470

1471
    if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1472

1473
    _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n");
1474
    }
1475

1476
    cmsDeleteTransform(xform);
1477
    return 1;
1478
}
1479

1480

1481

1482
// This one does create a Color Rendering Dictionary.
1483
// CRD are always LUT-Based, no matter if profile is
1484
// implemented as matrix-shaper.
1485

1486
static
1487
cmsUInt32Number  GenerateCRD(cmsContext ContextID,
1488
                             cmsHPROFILE hProfile,
1489
                             cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1490
                             cmsIOHANDLER* mem)
1491
{
1492
    cmsUInt32Number dwBytesUsed;
1493

1494
    if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1495

1496
        EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
1497
    }
1498

1499

1500
    // Is a named color profile?
1501
    if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) {
1502

1503
        if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
1504
            return 0;
1505
        }
1506
    }
1507
    else {
1508

1509
        // CRD are always implemented as LUT
1510

1511
        if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
1512
            return 0;
1513
        }
1514
    }
1515

1516
    if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
1517

1518
        _cmsIOPrintf(mem, "%%%%EndResource\n");
1519
        _cmsIOPrintf(mem, "\n%% CRD End\n");
1520
    }
1521

1522
    // Done, keep memory usage
1523
    dwBytesUsed = mem ->UsedSpace;
1524

1525
    // Finally, return used byte count
1526
    return dwBytesUsed;
1527

1528
    cmsUNUSED_PARAMETER(ContextID);
1529
}
1530

1531

1532

1533

1534
cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID,
1535
                                                               cmsPSResourceType Type,
1536
                                                               cmsHPROFILE hProfile,
1537
                                                               cmsUInt32Number Intent,
1538
                                                               cmsUInt32Number dwFlags,
1539
                                                               cmsIOHANDLER* io)
1540
{
1541
    cmsUInt32Number  rc;
1542

1543

1544
    switch (Type) {
1545

1546
        case cmsPS_RESOURCE_CSA:
1547
            rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io);
1548
            break;
1549

1550
        default:
1551
        case cmsPS_RESOURCE_CRD:
1552
            rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io);
1553
            break;
1554
    }
1555

1556
    return rc;
1557
}
1558

1559

1560

1561
cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID,
1562
                              cmsHPROFILE hProfile,
1563
                              cmsUInt32Number Intent, cmsUInt32Number dwFlags,
1564
                              void* Buffer, cmsUInt32Number dwBufferLen)
1565
{
1566
    cmsIOHANDLER* mem;
1567
    cmsUInt32Number dwBytesUsed;
1568

1569
    // Set up the serialization engine
1570
    if (Buffer == NULL)
1571
        mem = cmsOpenIOhandlerFromNULL(ContextID);
1572
    else
1573
        mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1574

1575
    if (!mem) return 0;
1576

1577
    dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem);
1578

1579
    // Get rid of memory stream
1580
    cmsCloseIOhandler(mem);
1581

1582
    return dwBytesUsed;
1583
}
1584

1585

1586

1587
// Does create a Color Space Array on XYZ colorspace for PostScript usage
1588
cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID,
1589
                                              cmsHPROFILE hProfile,
1590
                                              cmsUInt32Number Intent,
1591
                                              cmsUInt32Number dwFlags,
1592
                                              void* Buffer,
1593
                                              cmsUInt32Number dwBufferLen)
1594
{
1595
    cmsIOHANDLER* mem;
1596
    cmsUInt32Number dwBytesUsed;
1597

1598
    if (Buffer == NULL)
1599
        mem = cmsOpenIOhandlerFromNULL(ContextID);
1600
    else
1601
        mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w");
1602

1603
    if (!mem) return 0;
1604

1605
    dwBytesUsed =  cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem);
1606

1607
    // Get rid of memory stream
1608
    cmsCloseIOhandler(mem);
1609

1610
    return dwBytesUsed;
1611

1612
}
1613

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.