1 /*************************************************************************
2  *
3  *  OpenOffice.org - a multi-platform office productivity suite
4  *
5 
6   Derived by beppec56@openoffice.org from various examples
7   in SampleICC library, the original copyright retained.
8 
9   Copyright:  � see below
10 */
11 
12 /*
13  * The ICC Software License, Version 0.1
14  *
15  *
16  * Copyright (c) 2003-2006 The International Color Consortium. All rights
17  * reserved.
18  *
19  * Redistribution and use in source and binary forms, with or without
20  * modification, are permitted provided that the following conditions
21  * are met:
22  *
23  * 1. Redistributions of source code must retain the above copyright
24  *    notice, this list of conditions and the following disclaimer.
25  *
26  * 2. Redistributions in binary form must reproduce the above copyright
27  *    notice, this list of conditions and the following disclaimer in
28  *    the documentation and/or other materials provided with the
29  *    distribution.
30  *
31  * 3. The end-user documentation included with the redistribution,
32  *    if any, must include the following acknowledgment:
33  *       "This product includes software developed by the
34  *        The International Color Consortium (www.color.org)"
35  *    Alternately, this acknowledgment may appear in the software itself,
36  *    if and wherever such third-party acknowledgments normally appear.
37  *
38  * 4. The names "ICC" and "The International Color Consortium" must
39  *    not be used to imply that the ICC organization endorses or
40  *    promotes products derived from this software without prior
41  *    written permission. For written permission, please see
42  *    <http://www.color.org/>.
43  *
44  *
45  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
46  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
47  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
48  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNATIONAL COLOR CONSORTIUM OR
49  * ITS CONTRIBUTING MEMBERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
51  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
52  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
53  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
54  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
55  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56  * SUCH DAMAGE.
57  * ====================================================================
58  *
59  * This software consists of voluntary contributions made by many
60  * individuals on behalf of the The International Color Consortium.
61  *
62  *
63  * Membership in the ICC is encouraged when this software is used for
64  * commercial purposes.
65  *
66  *
67  * For more information on The International Color Consortium, please
68  * see <http://www.color.org/>.
69  *
70  *
71  */
72 
73 #include <math.h>
74 #include <iostream>
75 #include <fstream>
76 using namespace std;
77 
78 #include "IccUtil.h"
79 #include "IccProfile.h"
80 
81 #include "Vetters.h"
82 #include "CAT.h"
83 #include "CLUT.h"
84 
85 const char * const icc_file_name = "sRGB-IEC61966-2.1.icc";
86 const char * const hxx_file_name = "sRGB-IEC61966-2.1.hxx";
87 const char * const this_file_name_and_location =" * icc/source/create_sRGB_profile/create_sRGB_profile.cpp";
88 
89 const char* const description = "sRGB IEC61966-2.1";
90 //const char* const devicemanufact = "IEC http://www.iec.ch"; not used, device manufactured by OOo seems funny...
91 const char* const devicemodel = "IEC 61966-2.1 Default RGB colour space - sRGB";
92 const char* const copyright = "The Contents of this file are made available subject to the terms of GNU Lesser General Public License version 3";
93 
94 // the creation date is fixed, corresponds to the last time this file has been changed
95 // NOTE: change this date values whenever the data inside the profile are changed.
96 const int  data_last_changed_year =     2007;
97 const int  data_last_changed_month =    12;
98 const int  data_last_day =              12;
99 const int  data_last_changed_hour =     18;
100 const int  data_last_changed_minute =   32;
101 
102 // the following string array it's the standard OOo header format
103 const char * const TheHeader1[] =
104 {
105     "/*************************************************************************",
106     " *",
107     " *  OpenOffice.org - a multi-platform office productivity suite",
108     " *",
109     " *  sRGB-IEC61966-2.1.hxx",
110     " *",
111     " *  creator: create_sRGB_profile",
112     NULL
113 };
114 
115 const char * const TheHeader2[] =
116 {
117     " *",
118     " * This file is part of OpenOffice.org.",
119     " *",
120     " * OpenOffice.org is free software: you can redistribute it and/or modify",
121     " * it under the terms of the GNU Lesser General Public License version 3",
122     " * only, as published by the Free Software Foundation.",
123     " *",
124     " * OpenOffice.org is distributed in the hope that it will be useful,",
125     " * but WITHOUT ANY WARRANTY; without even the implied warranty of",
126     " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the",
127     " * GNU Lesser General Public License version 3 for more details",
128     " * (a copy is included in the LICENSE file that accompanied this code).",
129     " *",
130     " * You should have received a copy of the GNU Lesser General Public License",
131     " * version 3 along with OpenOffice.org.  If not, see",
132     " * <http://www.openoffice.org/license.html>",
133     " * for a copy of the LGPLv3 License.",
134     " *",
135     " ************************************************************************/",
136     "",
137     "#ifndef INCLUDED_ICC_SRGB_IEC61966_2_1_H",
138     "#define INCLUDED_ICC_SRGB_IEC61966_2_1_H",
139     "",
140     "/***********************************************************************",
141     " * NOTE:",
142     " * this file is automatically generated by running the program",
143     " * obtained building:",
144     this_file_name_and_location,
145     " * contained in module icc",
146     " * modify that program if you need to change something.",
147     " ***********************************************************************/",
148     NULL // last string, a null
149 };
150 
151 const char * const TheTail[] =
152 {
153     "#endif /* INCLUDED_ICC_SRGB_IEC61966_2_1_H */",
154     NULL
155 };
156 
157 icFloatNumber computeIEC_RGBtoXYZ( icFloatNumber indata )
158 {
159     double retval = 0.0;
160     if(indata < 0.04045)
161         retval = indata/12.92;
162     else // apply the other conversion
163         retval = pow( (indata + 0.055)/1.055 , 2.4);
164 
165     return retval;
166 }
167 
168 icFloatNumber computeIEC_XYZtoRGB( icFloatNumber indata )
169 {
170     icFloatNumber retval = 0.0;
171     if(indata < 0.0031308)
172         retval = indata*12.92;
173     else // apply the other conversion
174         retval =  1.055*pow( indata , icFloatNumber(1.0/2.4)) - 0.055;
175 
176     //  cout << retval << endl;
177     return retval;
178 }
179 
180 void dumpTag(FILE *outfile, CIccProfile *pIcc, icTagSignature sig)
181 {
182     CIccTag *pTag = pIcc->FindTag(sig);
183     char buf[64];
184     CIccInfo Fmt;
185 
186     std::string contents;
187 
188     if (pTag)
189     {
190         fprintf(outfile, "\nContents of %s tag (%s)\n", Fmt.GetTagSigName(sig), icGetSig(buf, sig));
191         fprintf(outfile,"Type:   ");
192 
193         if (pTag->IsArrayType())  fprintf(outfile, "Array of ");
194 
195         fprintf(outfile, "%s\n", Fmt.GetTagTypeSigName(pTag->GetType()));
196         pTag->Describe(contents);
197         fwrite(contents.c_str(), contents.length(), 1, outfile);
198     }
199     else
200         fprintf(outfile, "Tag (%s) not found in profile\n", icGetSig(buf, sig));
201 }
202 
203 void dumpProfile(FILE *outfile, const char * profileName)
204 {
205     CIccProfile *pIcc;
206     std::string sReport;
207     icValidateStatus nStatus;
208 
209     pIcc = OpenIccProfile(profileName);
210 
211     if (!pIcc)
212         printf("Unable to open '%s'\n", profileName);
213     else
214     {
215         icHeader *pHdr = &pIcc->m_Header;
216         CIccInfo Fmt;
217         char buf[64];
218 
219         fprintf(outfile,"Profile:          '%s'\n", profileName);
220         if(Fmt.IsProfileIDCalculated(&pHdr->profileID))
221             fprintf(outfile,"Profile ID:        %s\n", Fmt.GetProfileID(&pHdr->profileID));
222         else
223             fprintf(outfile,"Profile ID:       Profile ID not calculated.\n");
224         fprintf(outfile,"Size:             %ld(0x%lx) bytes\n", pHdr->size, pHdr->size);
225 
226         fprintf(outfile,"\nHeader\n");
227         fprintf(outfile,"------\n");
228         fprintf(outfile,"Attributes:       %s\n", Fmt.GetDeviceAttrName(pHdr->attributes));
229         fprintf(outfile,"Cmm:              %s\n", Fmt.GetCmmSigName((icCmmSignature)(pHdr->cmmId)));
230         fprintf(outfile,"Creation Date:    %d/%d/%d  %02u:%02u:%02u\n",
231                 pHdr->date.month, pHdr->date.day, pHdr->date.year,
232                 pHdr->date.hours, pHdr->date.minutes, pHdr->date.seconds);
233         fprintf(outfile,"Creator:          %s\n", icGetSig(buf, pHdr->creator));
234         fprintf(outfile,"Data Color Space: %s\n", Fmt.GetColorSpaceSigName(pHdr->colorSpace));
235         fprintf(outfile,"Flags             %s\n", Fmt.GetProfileFlagsName(pHdr->flags));
236         fprintf(outfile,"PCS Color Space:  %s\n", Fmt.GetColorSpaceSigName(pHdr->pcs));
237         fprintf(outfile,"Platform:         %s\n", Fmt.GetPlatformSigName(pHdr->platform));
238         fprintf(outfile,"Rendering Intent: %s\n", Fmt.GetRenderingIntentName((icRenderingIntent)(pHdr->renderingIntent)));
239         fprintf(outfile,"Type:             %s\n", Fmt.GetProfileClassSigName(pHdr->deviceClass));
240         fprintf(outfile,"Version:          %s\n", Fmt.GetVersionName(pHdr->version));
241         fprintf(outfile,"Illuminant:       X=%.4lf, Y=%.4lf, Z=%.4lf\n",
242                 icFtoD(pHdr->illuminant.X),
243                 icFtoD(pHdr->illuminant.Y),
244                 icFtoD(pHdr->illuminant.Z));
245 
246         fprintf(outfile,"\nProfile Tags\n");
247         fprintf(outfile,"------------\n");
248 
249         fprintf(outfile,"%25s    ID    %8s\t%8s\n", "Tag", "Offset", "Size");
250         fprintf(outfile,"%25s  ------  %8s\t%8s\n", "----", "------", "----");
251 
252         int n;
253         TagEntryList::iterator i;
254 
255         for (n=0, i=pIcc->m_Tags->begin(); i!=pIcc->m_Tags->end(); i++, n++)
256         {
257             fprintf(outfile,"%25s  %s  %8ld\t%8ld\n", Fmt.GetTagSigName(i->TagInfo.sig),
258                     icGetSig(buf, i->TagInfo.sig, false),
259                     i->TagInfo.offset, i->TagInfo.size);
260         }
261 
262         for (n=0, i=pIcc->m_Tags->begin(); i!=pIcc->m_Tags->end(); i++, n++)
263             dumpTag(outfile, pIcc, i->TagInfo.sig);
264     }
265     delete pIcc;
266 }
267 
268 int main(int argc, char* argv[])
269 {
270     const char* myName = path_tail(argv[0]);
271 
272     try
273     {
274         const char* const out_file_pathname = icc_file_name;
275 
276         CIccProfile profile;
277         profile.InitHeader();
278 
279         profile.m_Header.date.year = data_last_changed_year;
280         profile.m_Header.date.month = data_last_changed_month;
281         profile.m_Header.date.day = data_last_day;
282         profile.m_Header.date.hours = data_last_changed_hour;
283         profile.m_Header.date.minutes = data_last_changed_minute;
284         profile.m_Header.date.seconds = 0;
285 
286         profile.m_Header.deviceClass = icSigDisplayClass;
287         profile.m_Header.colorSpace = icSigRgbData;
288         profile.m_Header.pcs = icSigXYZData;
289         profile.m_Header.platform = icSigUnkownPlatform;
290         profile.m_Header.attributes = static_cast<icUInt64Number>(icReflective);
291         profile.m_Header.renderingIntent = icPerceptual;
292 
293         profile.m_Header.cmmId = 0x6E6F6E65; /* 'none' */
294         profile.m_Header.model = 0x73524742;//sRGB
295 
296         profile.m_Header.version=icVersionNumberV2_1;
297 
298         // Required tags for a three-component matrix-based display profile, as laid
299         // out by specification ICC.1:1998-09 (clause 6.3)  are:
300         //
301         //   copyrightTag
302         //   profileDescriptionTag
303         //   redMatrixColumnTag
304         //   greenMatrixColumnTag
305         //   blueMatrixColumnTag
306         //   redTRCTag
307         //   greenTRCTag
308         //   blueTRCTag
309         //   mediaWhitePointTag
310 
311         // the other tags:
312         //
313         // technologyTag
314         // deviceModelTag
315         // deviceMfgDescTag
316         // mediaBlackPointTag
317         // viewingCondDescTag
318         // viewingConditionsTag
319         // luminanceTag
320         // measurementTag
321         //
322         // are optionals, added for completeness
323 
324         // the element below are sorted in the same order as
325         // the list above, but the LUT table,
326         // embedded at the end of the profile
327 
328         // copyrightTag
329         CIccTagText* copyrightTag = new CIccTagText;
330         copyrightTag->SetText(copyright);
331         profile.AttachTag(icSigCopyrightTag, copyrightTag);
332 
333         // profileDescriptionTag
334         CIccTagTextDescription* descriptionTag = new CIccTagTextDescription;
335         descriptionTag->SetText(description);
336         profile.AttachTag(icSigProfileDescriptionTag, descriptionTag);
337 
338         CIccTagXYZ* redMatrixColumnTag = new CIccTagXYZ;
339         //values from raccomandation of ICC for sRGB, D50 referenced characterisation data
340         //should be: 0.4361, 0.2225, 0.0139 according to application notes,
341         // the 'X' value below is the one commonly in use on a very
342         // diffused sRGB profile
343         (*redMatrixColumnTag)[0].X = icDtoF(0.4361);
344         (*redMatrixColumnTag)[0].Y = icDtoF(0.2225);
345         (*redMatrixColumnTag)[0].Z = icDtoF(0.0139);
346         profile.AttachTag(icSigRedMatrixColumnTag, redMatrixColumnTag);
347 
348         CIccTagXYZ* greenMatrixColumnTag = new CIccTagXYZ;
349         //values from raccomandation of ICC for sRGB, D50 referenced characterisation data
350         (*greenMatrixColumnTag)[0].X = icDtoF(0.3851);
351         (*greenMatrixColumnTag)[0].Y = icDtoF(0.7169);
352         (*greenMatrixColumnTag)[0].Z = icDtoF(0.0971);
353         profile.AttachTag(icSigGreenMatrixColumnTag, greenMatrixColumnTag);
354 
355         CIccTagXYZ* blueMatrixColumnTag = new CIccTagXYZ;
356         //values from raccomandation of ICC for sRGB, D50 referenced characterisation data
357         //should be: 0.1431, 0.0606, 0.7139 according to application notes,
358         // the 'Z' value below is the one commonly in use on a very
359         // diffused sRGB profile
360         (*blueMatrixColumnTag)[0].X = icDtoF(0.1431);
361         (*blueMatrixColumnTag)[0].Y = icDtoF(0.0606);
362         (*blueMatrixColumnTag)[0].Z = icDtoF(0.7141);
363         profile.AttachTag(icSigBlueMatrixColumnTag, blueMatrixColumnTag);
364 
365         // mediaWhitePointTag
366         CIccTagXYZ* whitePointTag = new CIccTagXYZ;
367         (*whitePointTag)[0].X = icDtoF(0.9505);
368         (*whitePointTag)[0].Y = icDtoF(1.0);
369         (*whitePointTag)[0].Z = icDtoF(1.0891);
370         profile.AttachTag(icSigMediaWhitePointTag, whitePointTag);
371 
372         //device signature (technologytag)
373         CIccTagSignature* deviceSign = new CIccTagSignature;
374         deviceSign->SetValue( icSigCRTDisplay );
375         profile.AttachTag( icSigTechnologyTag, deviceSign );
376 
377         //device model tag
378         CIccTagTextDescription* deviceModelTag = new CIccTagTextDescription;
379         deviceModelTag->SetText("IEC 61966-2.1 Default RGB colour space - sRGB");
380         profile.AttachTag( icSigDeviceModelDescTag, deviceModelTag);
381 
382         // deviceMfgDescTag
383         CIccTagTextDescription* deviceMfgTag = new CIccTagTextDescription;
384         deviceMfgTag->SetText("IEC http://www.iec.ch");
385         profile.AttachTag( icSigDeviceMfgDescTag, deviceMfgTag);
386 
387         // mediaBlackPointTag
388         CIccTagXYZ* blackPointTag = new CIccTagXYZ;
389         (*blackPointTag)[0].X =
390         (*blackPointTag)[0].Y =
391         (*blackPointTag)[0].Z = icDtoF(0.0);
392         profile.AttachTag(icSigMediaBlackPointTag, blackPointTag);
393 
394         // viewingCondDescTag
395         CIccTagTextDescription* viewingCondDescTag = new CIccTagTextDescription;
396         viewingCondDescTag->SetText("Reference viewing condition according to IEC 61966-2.1");
397         profile.AttachTag( icSigViewingCondDescTag, viewingCondDescTag );
398 
399         // viewingConditionsTag
400         CIccTagViewingConditions* viewingConditionsTag = new  CIccTagViewingConditions;
401         // Illuminant tristimulus value
402         (*viewingConditionsTag).m_XYZIllum.X = icDtoF(19.6445);
403         (*viewingConditionsTag).m_XYZIllum.Y = icDtoF(20.3718);
404         (*viewingConditionsTag).m_XYZIllum.Z = icDtoF(16.8089);
405         // surround tristimulus value
406         (*viewingConditionsTag).m_XYZSurround.X = icDtoF(3.9289);
407         (*viewingConditionsTag).m_XYZSurround.Y = icDtoF(4.0744);
408         (*viewingConditionsTag).m_XYZSurround.Z = icDtoF(3.3618);
409         (*viewingConditionsTag).m_illumType = icIlluminantD50;
410         profile.AttachTag( icSigViewingConditionsType, viewingConditionsTag );
411 
412         // luminanceTag
413         CIccTagXYZ* luminanceTag = new CIccTagXYZ;
414         (*luminanceTag)[0].X = icDtoF(76.0365);
415         (*luminanceTag)[0].Y = icDtoF(80.0);
416         (*luminanceTag)[0].Z = icDtoF(87.1246);
417         profile.AttachTag(icSigLuminanceTag, luminanceTag);
418 
419         // measurementTag
420         CIccTagMeasurement* measurementTag = new  CIccTagMeasurement;
421         (*measurementTag).m_Data.stdObserver = icStdObs1931TwoDegrees;
422         (*measurementTag).m_Data.backing.X =
423         (*measurementTag).m_Data.backing.Y =
424         (*measurementTag).m_Data.backing.Z = icDtoF(0.0);
425         (*measurementTag).m_Data.geometry = icGeometryUnknown;
426         // the flare is 1%, but the library doesn't seem all right with this
427         // see specification ICC.1:1998-09, clause 6.5.8, table 55 fot the right
428         // format of the data value
429         (*measurementTag).m_Data.flare = static_cast< icMeasurementFlare > ( icDtoUF( 0.01 ) );//means 1%
430         (*measurementTag).m_Data.illuminant = icIlluminantD65;
431         profile.AttachTag(icSigMeasurementTag, measurementTag );
432 
433         // compute the LUT curves, they are equal for all three colors
434         // so only one LUT is computed and stored
435         int N = 1024; // number of points in LUTs
436         CIccTagCurve* colorTRCTag = new CIccTagCurve(N);
437         // apply conversion from RGB to XYZ, stepping the RGB value linearly from 0 to 100%
438         // 1024 steps are computed
439         for (int i = 0; i < N; ++i)
440             (*colorTRCTag)[i] = computeIEC_RGBtoXYZ( (icFloatNumber)i/(N-1));
441 
442         profile.AttachTag(icSigRedTRCTag, colorTRCTag);
443         profile.AttachTag(icSigGreenTRCTag, colorTRCTag);
444         profile.AttachTag(icSigBlueTRCTag, colorTRCTag);
445 
446         //Verify things
447         string validationReport;
448         icValidateStatus validationStatus = profile.Validate(validationReport);
449 
450         switch (validationStatus)
451         {
452         case icValidateOK:
453             break;
454 
455         case icValidateWarning:
456             clog << "Profile validation warning" << endl
457                  << validationReport;
458             break;
459 
460         case icValidateNonCompliant:
461             clog << "Profile non compliancy" << endl
462                  << validationReport;
463             break;
464 
465         case icValidateCriticalError:
466         default:
467             clog << "Profile Error" << endl
468                  << validationReport;
469         }
470 
471         // Out it goes
472         CIccFileIO out;
473         out.Open(out_file_pathname, "wb+");
474         profile.Write(&out);
475         out.Close();
476 
477         FILE *headerfile = fopen(hxx_file_name,"w");
478 
479         //print OpenOffice standard file header
480         const char *the_string;
481 
482         int idx = 0;
483 
484         while((the_string = TheHeader1[idx++]) != NULL )
485             fprintf(headerfile,"%s\n",the_string);
486 // print the creation date (today)
487 // print the date of sRGB ICC profile data
488         fprintf(headerfile," *  the date of last change to sRGB ICC profile data is:\n *  %4d/%02d/%02d - %02d:%02d\n",
489                 data_last_changed_year, data_last_changed_month,
490                 data_last_day, data_last_changed_hour,data_last_changed_minute );
491 
492         idx = 0;
493 
494         while((the_string = TheHeader2[idx++]) != NULL )
495             fprintf(headerfile,"%s\n",the_string);
496 
497         {
498 // spit out the data structure (an array of unsigned char)
499             FILE *infile;
500 
501             int achar, number = 1;
502 
503             infile = fopen(out_file_pathname,"rb");
504 
505             fseek(infile,0,SEEK_END);
506             long int thesize= ftell(infile);
507             fseek(infile,0,SEEK_SET);
508 
509             fprintf(headerfile,"\nsal_uInt8 nsRGB_ICC_profile[%d]=\n{\n    ",thesize);
510 
511             do
512             {
513                 achar = fgetc(infile);
514                 if(achar == EOF)
515                     break;
516                 fprintf(headerfile,"0x%02x",achar);
517                 if(number % 12 == 0)
518                     fprintf(headerfile,",\n    ");
519                 else
520                     fprintf(headerfile,", ");
521                 number++;
522             } while(achar != EOF );
523             fprintf(headerfile,"\n};\n\n");
524 
525             fclose(infile);
526         }
527         // append the file contents, in human readable form, as comment in the header
528         // get the functions from iccDump
529 
530         fprintf(headerfile,"/*****************\n\n");
531 
532         fprintf(headerfile,"This ICC profile contains the following data:\n\n");
533 
534         dumpProfile(headerfile, out_file_pathname );
535 
536         fprintf(headerfile,"\n*****************/\n");
537         //now append the tail
538         idx = 0;
539         while((the_string = TheTail[idx++]) != NULL )
540             fprintf(headerfile,"%s\n",the_string);
541 
542         fclose(headerfile);
543 
544         return EXIT_SUCCESS;
545     }
546     catch (const std::exception& e)
547     {
548         cout << myName << ": error: " << e.what() << endl;
549         return EXIT_FAILURE;
550     }
551 }
552