int main(int argc, char* argv[]) { const char* const my_name = path_tail(argv[0]); if (argc < 7 || argc > 8) { usage(cout, my_name); return EXIT_FAILURE; } try { const char* const edge_size_string = argv[1]; vet_as_int(edge_size_string, "N", "the length of the sides of the" " flattened probe cube"); size_t N = atoi(edge_size_string); if (N < 1) throw ICC_tool_exception("length of the sides of the flattened probe must" " be positive (and really should be at least 11," " preferably considerably more than that.)"); const char * const grabbed_probe_values_pathname = argv[2]; vet_input_file_pathname(grabbed_probe_values_pathname, "probe", "the pathname of the readable," " nonzero-length text file containing the values extracted from the" " screen grab of the flattened probe cube"); vector<DeviceRGB> device_RGBs (RGBs_from_probe_pathname(grabbed_probe_values_pathname)); const char* const monitor_profile_pathname = argv[3]; vet_input_file_pathname(monitor_profile_pathname, "monitor", "the pathname of the ICC profile which was active" " for the screen on which the flattened probe cube screen grab" " was made"); const char* const input_profile_description = argv[4]; const char* const input_profile_pathname = argv[5]; vet_output_file_pathname(input_profile_pathname, "path", "the pathname of the file to which the created CLUT input" " profile will be written, with the directory component of that" " pathname being writable by the current user"); const char* const copyright_holder = argv[6]; char year[5]; const time_t now = time(NULL); strftime(year, 5, "%G", localtime(&now)); const char* const copyright_pattern("copyright (c) %s %s"); char copyright[256]; sprintf(copyright, copyright_pattern, copyright_holder, year); bool pretransform_pathname_specified = argc == 8; const char* pretransform_pathname = ""; if (pretransform_pathname_specified) { pretransform_pathname = argv[7]; vet_input_file_pathname(pretransform_pathname, "pretransform", "pathname containing pretransform curve expressed as lines of triplets" " of shaper LUT contents in [0, 1024)"); } CIccProfile* monitor_profile = ReadIccProfile(monitor_profile_pathname); // +++ check result here CIccCmm* cmm = new CIccCmm(); icRenderingIntent monitor_rendering_intent = static_cast<icRenderingIntent>(monitor_profile->m_Header.renderingIntent); if (cmm->AddXform(monitor_profile_pathname, monitor_rendering_intent) != icCmmStatOk) { ostringstream s; s << "Can't set profile `" << monitor_profile_pathname << "' as initial CMM profile"; throw runtime_error(s.str()); } if (cmm->Begin() != icCmmStatOk) { throw runtime_error("Can't start CMM"); } vector<CIEXYZ> PCS_XYZs_from_probe(XYZs_from_monitor_RGBs(device_RGBs, cmm)); CIccProfile input_profile; input_profile.InitHeader(); input_profile.m_Header.deviceClass = icSigInputClass; input_profile.m_Header.platform = icSigMacintosh; input_profile.m_Header.colorSpace = icSigRgbData; input_profile.m_Header.pcs = icSigXYZData; input_profile.m_Header.attributes = static_cast<icUInt64Number>(icTransparency); input_profile.m_Header.renderingIntent = monitor_profile->m_Header.renderingIntent; // Required tags for an N-component LUT-based input profile, as layed out in // the ICC spec [sections 8.2 and 8.3.2] are: // profileDescriptionTag // copyrightTag // mediaWhitePointTag // chromaticAdaptationTag // A2B0 tag // As it happens, not only are those ordered by their appearance in section // 8.2 and 8.3.2, they are pretty much also ordered in increasing complexity. // profileDescriptionTag CIccLocalizedUnicode USAEnglishDesc; USAEnglishDesc.SetText((string("(grabbed) ") + input_profile_description).c_str()); CIccTagMultiLocalizedUnicode* descriptionTag = new CIccTagMultiLocalizedUnicode; descriptionTag->m_Strings = new CIccMultiLocalizedUnicode; // dtor does deletion descriptionTag->m_Strings->push_back(USAEnglishDesc); input_profile.AttachTag(icSigProfileDescriptionTag, descriptionTag); // copyrightTag CIccLocalizedUnicode USAEnglishCopyright; USAEnglishCopyright.SetText(copyright); CIccTagMultiLocalizedUnicode* copyrightTag = new CIccTagMultiLocalizedUnicode; copyrightTag->m_Strings = new CIccMultiLocalizedUnicode; // dtor does deletion copyrightTag->m_Strings->push_back(USAEnglishCopyright); input_profile.AttachTag(icSigCopyrightTag, copyrightTag); CIccTagXYZ* white_point_tag = static_cast<CIccTagXYZ*>(monitor_profile->FindTag(icSigMediaWhitePointTag)); input_profile.AttachTag(icSigMediaWhitePointTag, white_point_tag); CIccTagXYZ* black_point_tag = static_cast<CIccTagXYZ*>(monitor_profile->FindTag(icSigMediaBlackPointTag)); // This is not a required tag, but if it's there, carry it over. if (black_point_tag != NULL) input_profile.AttachTag(icSigMediaBlackPointTag, black_point_tag); CIccTagXYZ* luminance_tag = static_cast<CIccTagXYZ*>(monitor_profile->FindTag(icSigLuminanceTag)); if (luminance_tag != NULL) input_profile.AttachTag(icSigLuminanceTag, luminance_tag); CIccTagS15Fixed16* monitor_chromatic_adaptation_tag = static_cast<CIccTagS15Fixed16*>(monitor_profile->FindTag(icSigChromaticAdaptationTag)); input_profile.AttachTag(icSigChromaticAdaptationTag, monitor_chromatic_adaptation_tag); if (PCS_XYZs_from_probe.size() != N * N * N) { ostringstream s; s << "Edge size " << N << " implies a probe file with " << N *N * N << " entries, but the file provided contains " << PCS_XYZs_from_probe.size(); throw runtime_error(s.str()); } // +++ input shaper LUT someday CIccTagLut16* A2B0_tag = make_A2Bx_tag(N, PCS_XYZs_from_probe, pretransform_pathname); input_profile.AttachTag(icSigAToB0Tag, A2B0_tag); // CLUT* AToB0CLUT = new CLUT(); // CIccTagLut16* AToB0Tag // = AToB0CLUT->makeAToBxTag(size, measuredXYZ, flare, illuminant, CATToD50, // inputShaperGamma, inputShaperFilename, // adaptedMediaWhite, LABPCS); // BlackScaler blackScaler(size, measuredXYZ, adaptedMediaBlack, adaptedMediaWhite); // AToB0CLUT->Iterate(&blackScaler); // profile.AttachTag(icSigAToB0Tag, AToB0Tag); // the A2B0 tag //Verify things string validationReport; icValidateStatus validationStatus = input_profile.Validate(validationReport); switch (validationStatus) { case icValidateOK: break; case icValidateWarning: clog << "Profile validation warning" << endl << validationReport; break; case icValidateNonCompliant: clog << "Profile non compliancy" << endl << validationReport; break; case icValidateCriticalError: default: clog << "Profile Error" << endl << validationReport; } // Out it goes CIccFileIO out; out.Open(input_profile_pathname, "w+"); input_profile.Write(&out); out.Close(); return EXIT_SUCCESS; } catch (const exception& e) { cout << "error: " << e.what() << endl; return EXIT_FAILURE; } }
bool Apply(const char *szSrcImage, const char *szSrcProfile, const char *szDstProfile, const char *szDstImage, int nIntent) { unsigned long i, j, k, sn, sphoto, dn, photo, space; CTiffImg SrcImg, DstImg; CIccCmm cmm; unsigned char *sptr, *dptr; bool bSuccess = true; bool bConvert = false; if (cmm.AddXform(szSrcProfile, nIntent<0 ? icUnknownIntent : (icRenderingIntent)nIntent/*, icInterpTetrahedral*/)) { printf("Invalid Profile: %s\n", szSrcProfile); return false; } if (szDstProfile && *szDstProfile && cmm.AddXform(szDstProfile/*, icUnknownIntent, icInterpTetrahedral*/)) { printf("Invalid Profile: %s\n", szDstProfile); return false; } if (cmm.Begin() != icCmmStatOk) { printf("Invalid Profile:\n %s\n %s'\n", szSrcProfile, szDstProfile); return false; } if (!SrcImg.Open(szSrcImage)) { printf("Invalid Tiff file - '%s'\n", szSrcImage); return false; } sn = SrcImg.GetSamples(); sphoto = SrcImg.GetPhoto(); space = cmm.GetSourceSpace(); if (SrcImg.GetBitsPerSample()!=8 || !((space==icSigRgbData && sn==3 && sphoto==PHOTO_MINISBLACK) || (space==icSigLabData && sn==3 && sphoto==PHOTO_CIELAB) || (space==icSigXYZData && sn==3 && sphoto==PHOTO_CIELAB) || (space==icSigCmykData && sn==4 && sphoto==PHOTO_MINISWHITE) || (space==icSigMCH4Data && sn==4 && sphoto==PHOTO_MINISWHITE) || (space==icSigMCH5Data && sn==5 && sphoto==PHOTO_MINISWHITE) || (space==icSigMCH6Data && sn==6 && sphoto==PHOTO_MINISWHITE))) { printf("Invalid source profile/image pixel format\n"); return false; } switch (cmm.GetDestSpace()) { case icSigRgbData: photo = PHOTO_MINISBLACK; dn = 3; break; case icSigCmyData: photo = PHOTO_MINISWHITE; dn = 3; break; case icSigXYZData: bConvert = true; //Fall through - No break here case icSigLabData: photo = PHOTO_CIELAB; dn = 3; break; case icSigCmykData: case icSig4colorData: photo = PHOTO_MINISWHITE; dn = 4; break; case icSig5colorData: photo = PHOTO_MINISWHITE; dn = 5; break; case icSig6colorData: photo = PHOTO_MINISWHITE; dn = 6; break; case icSig7colorData: photo = PHOTO_MINISWHITE; dn = 7; break; case icSig8colorData: photo = PHOTO_MINISWHITE; dn = 8; break; default: printf("Invalid destination profile/image pixel format\n"); return false; } if (!DstImg.Create(szDstImage, SrcImg.GetWidth(), SrcImg.GetHeight(), 8, photo, dn, SrcImg.GetXRes(), SrcImg.GetYRes(), false)) { printf("Unable to create Tiff file - '%s'\n", szDstImage); return false; } unsigned char *pSBuf = (unsigned char *)malloc(SrcImg.GetBytesPerLine()); unsigned char *pDBuf = (unsigned char *)malloc(DstImg.GetBytesPerLine()); icFloatNumber Pixel[16]; if (!pSBuf) { printf("Out of Memory!\n"); return false; } if (!pDBuf) { printf("Out of Memory!\n"); free(pSBuf); return false; } for (i=0; i<SrcImg.GetHeight(); i++) { if (!SrcImg.ReadLine(pSBuf)) { bSuccess = false; break; } for (sptr=pSBuf, dptr=pDBuf, j=0; j<SrcImg.GetWidth(); j++, sptr+=sn, dptr+=dn) { if (sphoto==PHOTO_CIELAB) { Pixel[0] = (icFloatNumber)sptr[0] / 255.0f; Pixel[1] = ((icFloatNumber)((signed char)sptr[1]) + 128.0f) / 255.0f; Pixel[2] = ((icFloatNumber)((signed char)sptr[2]) + 128.0f) / 255.0f; if (space==icSigXYZData) { icLabFromPcs(Pixel); icLabtoXYZ(Pixel); icXyzToPcs(Pixel); } } else { for (k=0; k<sn; k++) { Pixel[k] = (icFloatNumber)sptr[k] / 255.0f; } } cmm.Apply(Pixel, Pixel); if (photo==PHOTO_CIELAB) { if (bConvert) { icXyzFromPcs(Pixel); icXYZtoLab(Pixel); icLabToPcs(Pixel); } dptr[0] = (unsigned char)(UnitClip(Pixel[0]) * 255.0 + 0.5); dptr[1] = (unsigned char)(UnitClip(Pixel[1]) * 255.0 - 128.0); dptr[2] = (unsigned char)(UnitClip(Pixel[2]) * 255.0 - 128.0); } else { for (k=0; k<dn; k++) { dptr[k] = (unsigned char)(UnitClip(Pixel[k]) * 255.0 + 0.5); } } } if (!DstImg.WriteLine(pDBuf)) { bSuccess = false; break; } } SrcImg.Close(); free(pSBuf); free(pDBuf); return bSuccess; }