#ifdef HAVE_CONFIG_H # include "config.h" #endif #define noDEBUG_LCMS #include <glib/gstdio.h> #include <sys/fcntl.h> #include <gdkmm/color.h> #include <glib/gi18n.h> #ifdef DEBUG_LCMS #include <gtk/gtkmessagedialog.h> #endif // DEBUG_LCMS #include <cstring> #include <string> #ifdef WIN32 #ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. Required for correctly including icm.h #define _WIN32_WINDOWS 0x0410 #endif #include <windows.h> #endif #include "xml/repr.h" #include "color.h" #include "color-profile.h" #include "color-profile-fns.h" #include "attributes.h" #include "inkscape.h" #include "document.h" #include "preferences.h" #include "dom/uri.h" #include "dom/util/digest.h" #ifdef WIN32 #include <icm.h> #endif // WIN32 using Inkscape::ColorProfile; using Inkscape::ColorProfileClass; namespace Inkscape { #if ENABLE_LCMS static cmsHPROFILE colorprofile_get_system_profile_handle(); static cmsHPROFILE colorprofile_get_proof_profile_handle(); #endif // ENABLE_LCMS } #ifdef DEBUG_LCMS extern guint update_in_progress; #define DEBUG_MESSAGE_SCISLAC(key, ...) \ {\ Inkscape::Preferences *prefs = Inkscape::Preferences::get();\ bool dump = prefs->getBool(Glib::ustring("/options/scislac/") + #key);\ bool dumpD = prefs->getBool(Glib::ustring("/options/scislac/") + #key"D");\ bool dumpD2 = prefs->getBool(Glib::ustring("/options/scislac/") + #key"D2");\ dumpD &= ( (update_in_progress == 0) || dumpD2 );\ if ( dump )\ {\ g_message( __VA_ARGS__ );\ \ }\ if ( dumpD )\ {\ GtkWidget *dialog = gtk_message_dialog_new(NULL,\ GTK_DIALOG_DESTROY_WITH_PARENT, \ GTK_MESSAGE_INFO, \ GTK_BUTTONS_OK, \ __VA_ARGS__ \ );\ g_signal_connect_swapped(dialog, "response",\ G_CALLBACK(gtk_widget_destroy), \ dialog); \ gtk_widget_show_all( dialog );\ }\ } #define DEBUG_MESSAGE(key, ...)\ {\ g_message( __VA_ARGS__ );\ } #endif // DEBUG_LCMS static SPObjectClass *cprof_parent_class; #if ENABLE_LCMS cmsHPROFILE ColorProfile::_sRGBProf = 0; cmsHPROFILE ColorProfile::getSRGBProfile() { if ( !_sRGBProf ) { _sRGBProf = cmsCreate_sRGBProfile(); } return _sRGBProf; } cmsHPROFILE ColorProfile::_NullProf = 0; cmsHPROFILE ColorProfile::getNULLProfile() { if ( !_NullProf ) { _NullProf = cmsCreateNULLProfile(); } return _NullProf; } #endif // ENABLE_LCMS /** * Register ColorProfile class and return its type. */ 00117 GType Inkscape::colorprofile_get_type() { return ColorProfile::getType(); } GType ColorProfile::getType() { static GType type = 0; if (!type) { GTypeInfo info = { sizeof(ColorProfileClass), NULL, NULL, (GClassInitFunc) ColorProfile::classInit, NULL, NULL, sizeof(ColorProfile), 16, (GInstanceInitFunc) ColorProfile::init, NULL, /* value_table */ }; type = g_type_register_static( SP_TYPE_OBJECT, "ColorProfile", &info, static_cast<GTypeFlags>(0) ); } return type; } /** * ColorProfile vtable initialization. */ 00144 void ColorProfile::classInit( ColorProfileClass *klass ) { SPObjectClass *sp_object_class = reinterpret_cast<SPObjectClass *>(klass); cprof_parent_class = static_cast<SPObjectClass*>(g_type_class_ref(SP_TYPE_OBJECT)); sp_object_class->release = ColorProfile::release; sp_object_class->build = ColorProfile::build; sp_object_class->set = ColorProfile::set; sp_object_class->write = ColorProfile::write; } /** * Callback for ColorProfile object initialization. */ 00159 void ColorProfile::init( ColorProfile *cprof ) { cprof->href = 0; cprof->local = 0; cprof->name = 0; cprof->intentStr = 0; cprof->rendering_intent = Inkscape::RENDERING_INTENT_UNKNOWN; #if ENABLE_LCMS cprof->profHandle = 0; cprof->_profileClass = icSigInputClass; cprof->_profileSpace = icSigRgbData; cprof->_transf = 0; cprof->_revTransf = 0; cprof->_gamutTransf = 0; #endif // ENABLE_LCMS } /** * Callback: free object */ 00179 void ColorProfile::release( SPObject *object ) { // Unregister ourselves SPDocument* document = SP_OBJECT_DOCUMENT(object); if ( document ) { sp_document_remove_resource (SP_OBJECT_DOCUMENT (object), "iccprofile", SP_OBJECT (object)); } ColorProfile *cprof = COLORPROFILE(object); if ( cprof->href ) { g_free( cprof->href ); cprof->href = 0; } if ( cprof->local ) { g_free( cprof->local ); cprof->local = 0; } if ( cprof->name ) { g_free( cprof->name ); cprof->name = 0; } if ( cprof->intentStr ) { g_free( cprof->intentStr ); cprof->intentStr = 0; } #if ENABLE_LCMS cprof->_clearProfile(); #endif // ENABLE_LCMS } #if ENABLE_LCMS void ColorProfile::_clearProfile() { _profileSpace = icSigRgbData; if ( _transf ) { cmsDeleteTransform( _transf ); _transf = 0; } if ( _revTransf ) { cmsDeleteTransform( _revTransf ); _revTransf = 0; } if ( _gamutTransf ) { cmsDeleteTransform( _gamutTransf ); _gamutTransf = 0; } if ( profHandle ) { cmsCloseProfile( profHandle ); profHandle = 0; } } #endif // ENABLE_LCMS /** * Callback: set attributes from associated repr. */ 00240 void ColorProfile::build( SPObject *object, SPDocument *document, Inkscape::XML::Node *repr ) { ColorProfile *cprof = COLORPROFILE(object); g_assert(cprof->href == 0); g_assert(cprof->local == 0); g_assert(cprof->name == 0); g_assert(cprof->intentStr == 0); if (cprof_parent_class->build) { (* cprof_parent_class->build)(object, document, repr); } sp_object_read_attr( object, "xlink:href" ); sp_object_read_attr( object, "local" ); sp_object_read_attr( object, "name" ); sp_object_read_attr( object, "rendering-intent" ); // Register if ( document ) { sp_document_add_resource( document, "iccprofile", object ); } } /** * Callback: set attribute. */ 00265 void ColorProfile::set( SPObject *object, unsigned key, gchar const *value ) { ColorProfile *cprof = COLORPROFILE(object); switch (key) { case SP_ATTR_XLINK_HREF: if ( cprof->href ) { g_free( cprof->href ); cprof->href = 0; } if ( value ) { cprof->href = g_strdup( value ); if ( *cprof->href ) { #if ENABLE_LCMS cmsErrorAction( LCMS_ERROR_SHOW ); // TODO open filename and URIs properly //FILE* fp = fopen_utf8name( filename, "r" ); //LCMSAPI cmsHPROFILE LCMSEXPORT cmsOpenProfileFromMem(LPVOID MemPtr, DWORD dwSize); // Try to open relative SPDocument *doc = SP_OBJECT_DOCUMENT(object); if (!doc) { doc = SP_ACTIVE_DOCUMENT; g_warning("object has no document. using active"); } //# 1. Get complete URI of document gchar const *docbase = SP_DOCUMENT_URI( doc ); if (!docbase) { // Normal for files that have not yet been saved. docbase = ""; } gchar* escaped = g_uri_escape_string(cprof->href, "!*'();:@=+$,/?#[]", TRUE); //g_message("docbase:%s\n", docbase); org::w3c::dom::URI docUri(docbase); //# 2. Get href of icc file. we don't care if it's rel or abs org::w3c::dom::URI hrefUri(escaped); //# 3. Resolve the href according the docBase. This follows // the w3c specs. All absolute and relative issues are considered org::w3c::dom::URI cprofUri = docUri.resolve(hrefUri); gchar* fullname = g_uri_unescape_string(cprofUri.getNativePath().c_str(), ""); cprof->_clearProfile(); cprof->profHandle = cmsOpenProfileFromFile( fullname, "r" ); if ( cprof->profHandle ) { cprof->_profileSpace = cmsGetColorSpace( cprof->profHandle ); cprof->_profileClass = cmsGetDeviceClass( cprof->profHandle ); } #ifdef DEBUG_LCMS DEBUG_MESSAGE( lcmsOne, "cmsOpenProfileFromFile( '%s'...) = %p", fullname, (void*)cprof->profHandle ); #endif // DEBUG_LCMS g_free(escaped); escaped = 0; g_free(fullname); #endif // ENABLE_LCMS } } object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_LOCAL: if ( cprof->local ) { g_free( cprof->local ); cprof->local = 0; } cprof->local = g_strdup( value ); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_NAME: if ( cprof->name ) { g_free( cprof->name ); cprof->name = 0; } cprof->name = g_strdup( value ); #ifdef DEBUG_LCMS DEBUG_MESSAGE( lcmsTwo, "<color-profile> name set to '%s'", cprof->name ); #endif // DEBUG_LCMS object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_RENDERING_INTENT: if ( cprof->intentStr ) { g_free( cprof->intentStr ); cprof->intentStr = 0; } cprof->intentStr = g_strdup( value ); if ( value ) { if ( strcmp( value, "auto" ) == 0 ) { cprof->rendering_intent = RENDERING_INTENT_AUTO; } else if ( strcmp( value, "perceptual" ) == 0 ) { cprof->rendering_intent = RENDERING_INTENT_PERCEPTUAL; } else if ( strcmp( value, "relative-colorimetric" ) == 0 ) { cprof->rendering_intent = RENDERING_INTENT_RELATIVE_COLORIMETRIC; } else if ( strcmp( value, "saturation" ) == 0 ) { cprof->rendering_intent = RENDERING_INTENT_SATURATION; } else if ( strcmp( value, "absolute-colorimetric" ) == 0 ) { cprof->rendering_intent = RENDERING_INTENT_ABSOLUTE_COLORIMETRIC; } else { cprof->rendering_intent = RENDERING_INTENT_UNKNOWN; } } else { cprof->rendering_intent = RENDERING_INTENT_UNKNOWN; } object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; default: if (cprof_parent_class->set) { (* cprof_parent_class->set)(object, key, value); } break; } } /** * Callback: write attributes to associated repr. */ 00388 Inkscape::XML::Node* ColorProfile::write( SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags ) { ColorProfile *cprof = COLORPROFILE(object); if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { repr = xml_doc->createElement("svg:color-profile"); } if ( (flags & SP_OBJECT_WRITE_ALL) || cprof->href ) { repr->setAttribute( "xlink:href", cprof->href ); } if ( (flags & SP_OBJECT_WRITE_ALL) || cprof->local ) { repr->setAttribute( "local", cprof->local ); } if ( (flags & SP_OBJECT_WRITE_ALL) || cprof->name ) { repr->setAttribute( "name", cprof->name ); } if ( (flags & SP_OBJECT_WRITE_ALL) || cprof->intentStr ) { repr->setAttribute( "rendering-intent", cprof->intentStr ); } if (cprof_parent_class->write) { (* cprof_parent_class->write)(object, xml_doc, repr, flags); } return repr; } #if ENABLE_LCMS struct MapMap { icColorSpaceSignature space; DWORD inForm; }; DWORD ColorProfile::_getInputFormat( icColorSpaceSignature space ) { MapMap possible[] = { {icSigXYZData, TYPE_XYZ_16}, {icSigLabData, TYPE_Lab_16}, //icSigLuvData {icSigYCbCrData, TYPE_YCbCr_16}, {icSigYxyData, TYPE_Yxy_16}, {icSigRgbData, TYPE_RGB_16}, {icSigGrayData, TYPE_GRAY_16}, {icSigHsvData, TYPE_HSV_16}, {icSigHlsData, TYPE_HLS_16}, {icSigCmykData, TYPE_CMYK_16}, {icSigCmyData, TYPE_CMY_16}, }; int index = 0; for ( guint i = 0; i < G_N_ELEMENTS(possible); i++ ) { if ( possible[i].space == space ) { index = i; break; } } return possible[index].inForm; } static int getLcmsIntent( guint svgIntent ) { int intent = INTENT_PERCEPTUAL; switch ( svgIntent ) { case Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC: intent = INTENT_RELATIVE_COLORIMETRIC; break; case Inkscape::RENDERING_INTENT_SATURATION: intent = INTENT_SATURATION; break; case Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC: intent = INTENT_ABSOLUTE_COLORIMETRIC; break; case Inkscape::RENDERING_INTENT_PERCEPTUAL: case Inkscape::RENDERING_INTENT_UNKNOWN: case Inkscape::RENDERING_INTENT_AUTO: default: intent = INTENT_PERCEPTUAL; } return intent; } static SPObject* bruteFind( SPDocument* document, gchar const* name ) { SPObject* result = 0; const GSList * current = sp_document_get_resource_list(document, "iccprofile"); while ( current && !result ) { if ( IS_COLORPROFILE(current->data) ) { ColorProfile* prof = COLORPROFILE(current->data); if ( prof ) { if ( prof->name && (strcmp(prof->name, name) == 0) ) { result = SP_OBJECT(current->data); break; } } } current = g_slist_next(current); } return result; } cmsHPROFILE Inkscape::colorprofile_get_handle( SPDocument* document, guint* intent, gchar const* name ) { cmsHPROFILE prof = 0; SPObject* thing = bruteFind( document, name ); if ( thing ) { prof = COLORPROFILE(thing)->profHandle; } if ( intent ) { *intent = thing ? COLORPROFILE(thing)->rendering_intent : (guint)RENDERING_INTENT_UNKNOWN; } #ifdef DEBUG_LCMS DEBUG_MESSAGE( lcmsThree, "<color-profile> queried for profile of '%s'. Returning %p with intent of %d", name, prof, (intent? *intent:0) ); #endif // DEBUG_LCMS return prof; } cmsHTRANSFORM ColorProfile::getTransfToSRGB8() { if ( !_transf && profHandle ) { int intent = getLcmsIntent(rendering_intent); _transf = cmsCreateTransform( profHandle, _getInputFormat(_profileSpace), getSRGBProfile(), TYPE_RGBA_8, intent, 0 ); } return _transf; } cmsHTRANSFORM ColorProfile::getTransfFromSRGB8() { if ( !_revTransf && profHandle ) { int intent = getLcmsIntent(rendering_intent); _revTransf = cmsCreateTransform( getSRGBProfile(), TYPE_RGBA_8, profHandle, _getInputFormat(_profileSpace), intent, 0 ); } return _revTransf; } cmsHTRANSFORM ColorProfile::getTransfGamutCheck() { if ( !_gamutTransf ) { _gamutTransf = cmsCreateProofingTransform(getSRGBProfile(), TYPE_RGBA_8, getNULLProfile(), TYPE_GRAY_8, profHandle, INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, (cmsFLAGS_GAMUTCHECK|cmsFLAGS_SOFTPROOFING)); } return _gamutTransf; } bool ColorProfile::GamutCheck(SPColor color){ BYTE outofgamut = 0; guint32 val = color.toRGBA32(0); guchar check_color[4] = { SP_RGBA32_R_U(val), SP_RGBA32_G_U(val), SP_RGBA32_B_U(val), 255}; int alarm_r, alarm_g, alarm_b; cmsGetAlarmCodes(&alarm_r, &alarm_g, &alarm_b); cmsSetAlarmCodes(255, 255, 255); cmsDoTransform(ColorProfile::getTransfGamutCheck(), &check_color, &outofgamut, 1); cmsSetAlarmCodes(alarm_r, alarm_g, alarm_b); return (outofgamut == 255); } #include <io/sys.h> class ProfileInfo { public: ProfileInfo( cmsHPROFILE, Glib::ustring const & path ); Glib::ustring const& getName() {return _name;} Glib::ustring const& getPath() {return _path;} icColorSpaceSignature getSpace() {return _profileSpace;} icProfileClassSignature getClass() {return _profileClass;} private: Glib::ustring _path; Glib::ustring _name; icColorSpaceSignature _profileSpace; icProfileClassSignature _profileClass; }; ProfileInfo::ProfileInfo( cmsHPROFILE prof, Glib::ustring const & path ) { _path = path; _name = cmsTakeProductDesc(prof); _profileSpace = cmsGetColorSpace( prof ); _profileClass = cmsGetDeviceClass( prof ); } static std::vector<ProfileInfo> knownProfiles; std::vector<Glib::ustring> Inkscape::colorprofile_get_display_names() { std::vector<Glib::ustring> result; for ( std::vector<ProfileInfo>::iterator it = knownProfiles.begin(); it != knownProfiles.end(); ++it ) { if ( it->getClass() == icSigDisplayClass && it->getSpace() == icSigRgbData ) { result.push_back( it->getName() ); } } return result; } std::vector<Glib::ustring> Inkscape::colorprofile_get_softproof_names() { std::vector<Glib::ustring> result; for ( std::vector<ProfileInfo>::iterator it = knownProfiles.begin(); it != knownProfiles.end(); ++it ) { if ( it->getClass() == icSigOutputClass ) { result.push_back( it->getName() ); } } return result; } Glib::ustring Inkscape::get_path_for_profile(Glib::ustring const& name) { Glib::ustring result; for ( std::vector<ProfileInfo>::iterator it = knownProfiles.begin(); it != knownProfiles.end(); ++it ) { if ( name == it->getName() ) { result = it->getPath(); break; } } return result; } #endif // ENABLE_LCMS std::list<Glib::ustring> ColorProfile::getBaseProfileDirs() { #if ENABLE_LCMS static bool warnSet = false; if (!warnSet) { cmsErrorAction( LCMS_ERROR_SHOW ); warnSet = true; } #endif // ENABLE_LCMS std::list<Glib::ustring> sources; gchar* base = profile_path("XXX"); { gchar* base2 = g_path_get_dirname(base); g_free(base); base = base2; base2 = g_path_get_dirname(base); g_free(base); base = base2; } // first try user's local dir sources.push_back( g_build_filename(g_get_user_data_dir(), "color", "icc", NULL) ); const gchar* const * dataDirs = g_get_system_data_dirs(); for ( int i = 0; dataDirs[i]; i++ ) { gchar* path = g_build_filename(dataDirs[i], "color", "icc", NULL); sources.push_back(path); g_free(path); } // On OS X: { bool onOSX = false; std::list<Glib::ustring> possible; possible.push_back("/System/Library/ColorSync/Profiles"); possible.push_back("/Library/ColorSync/Profiles"); for ( std::list<Glib::ustring>::const_iterator it = possible.begin(); it != possible.end(); ++it ) { if ( g_file_test(it->c_str(), G_FILE_TEST_EXISTS) && g_file_test(it->c_str(), G_FILE_TEST_IS_DIR) ) { sources.push_back(it->c_str()); onOSX = true; } } if ( onOSX ) { gchar* path = g_build_filename(g_get_home_dir(), "Library", "ColorSync", "Profiles", NULL); if ( g_file_test(path, G_FILE_TEST_EXISTS) && g_file_test(path, G_FILE_TEST_IS_DIR) ) { sources.push_back(path); } g_free(path); } } #ifdef WIN32 wchar_t pathBuf[MAX_PATH + 1]; pathBuf[0] = 0; DWORD pathSize = sizeof(pathBuf); g_assert(sizeof(wchar_t) == sizeof(gunichar2)); if ( GetColorDirectoryW( NULL, pathBuf, &pathSize ) ) { gchar * utf8Path = g_utf16_to_utf8( (gunichar2*)(&pathBuf[0]), -1, NULL, NULL, NULL ); if ( !g_utf8_validate(utf8Path, -1, NULL) ) { g_warning( "GetColorDirectoryW() resulted in invalid UTF-8" ); } else { sources.push_back(utf8Path); } g_free( utf8Path ); } #endif // WIN32 return sources; } static bool isIccFile( gchar const *filepath ) { bool isIccFile = false; struct stat st; if ( g_stat(filepath, &st) == 0 && (st.st_size > 128) ) { //0-3 == size //36-39 == 'acsp' 0x61637370 int fd = g_open( filepath, O_RDONLY, S_IRWXU); if ( fd != -1 ) { guchar scratch[40] = {0}; size_t len = sizeof(scratch); //size_t left = 40; ssize_t got = read(fd, scratch, len); if ( got != -1 ) { size_t calcSize = (scratch[0] << 24) | (scratch[1] << 16) | (scratch[2] << 8) | scratch[3]; if ( calcSize > 128 && calcSize <= static_cast<size_t>(st.st_size) ) { isIccFile = (scratch[36] == 'a') && (scratch[37] == 'c') && (scratch[38] == 's') && (scratch[39] == 'p'); } } close(fd); #if ENABLE_LCMS if (isIccFile) { cmsHPROFILE prof = cmsOpenProfileFromFile( filepath, "r" ); if ( prof ) { icProfileClassSignature profClass = cmsGetDeviceClass(prof); if ( profClass == icSigNamedColorClass ) { isIccFile = false; // Ignore named color profiles for now. } cmsCloseProfile( prof ); } } #endif // ENABLE_LCMS } } return isIccFile; } std::list<Glib::ustring> ColorProfile::getProfileFiles() { std::list<Glib::ustring> files; std::list<Glib::ustring> sources = ColorProfile::getBaseProfileDirs(); for ( std::list<Glib::ustring>::const_iterator it = sources.begin(); it != sources.end(); ++it ) { if ( g_file_test( it->c_str(), G_FILE_TEST_EXISTS ) && g_file_test( it->c_str(), G_FILE_TEST_IS_DIR ) ) { GError *err = 0; GDir *dir = g_dir_open(it->c_str(), 0, &err); if (dir) { for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) { gchar *filepath = g_build_filename(it->c_str(), file, NULL); if ( g_file_test( filepath, G_FILE_TEST_IS_DIR ) ) { sources.push_back(g_strdup(filepath)); } else { if ( isIccFile( filepath ) ) { files.push_back( filepath ); } } g_free(filepath); } g_dir_close(dir); dir = 0; } else { gchar *safeDir = Inkscape::IO::sanitizeString(it->c_str()); g_warning(_("Color profiles directory (%s) is unavailable."), safeDir); g_free(safeDir); } } } return files; } #if ENABLE_LCMS static void findThings() { std::list<Glib::ustring> files = ColorProfile::getProfileFiles(); for ( std::list<Glib::ustring>::const_iterator it = files.begin(); it != files.end(); ++it ) { cmsHPROFILE prof = cmsOpenProfileFromFile( it->c_str(), "r" ); if ( prof ) { ProfileInfo info( prof, Glib::filename_to_utf8( it->c_str() ) ); cmsCloseProfile( prof ); bool sameName = false; for ( std::vector<ProfileInfo>::iterator it = knownProfiles.begin(); it != knownProfiles.end(); ++it ) { if ( it->getName() == info.getName() ) { sameName = true; break; } } if ( !sameName ) { knownProfiles.push_back(info); } } } } int errorHandlerCB(int ErrorCode, const char *ErrorText) { g_message("lcms: Error %d; %s", ErrorCode, ErrorText); return 1; } static bool gamutWarn = false; static Gdk::Color lastGamutColor("#808080"); static bool lastBPC = false; #if defined(cmsFLAGS_PRESERVEBLACK) static bool lastPreserveBlack = false; #endif // defined(cmsFLAGS_PRESERVEBLACK) static int lastIntent = INTENT_PERCEPTUAL; static int lastProofIntent = INTENT_PERCEPTUAL; static cmsHTRANSFORM transf = 0; cmsHPROFILE Inkscape::colorprofile_get_system_profile_handle() { static cmsHPROFILE theOne = 0; static Glib::ustring lastURI; static bool init = false; if ( !init ) { cmsSetErrorHandler(errorHandlerCB); findThings(); init = true; } Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Glib::ustring uri = prefs->getString("/options/displayprofile/uri"); if ( !uri.empty() ) { if ( uri != lastURI ) { lastURI.clear(); if ( theOne ) { cmsCloseProfile( theOne ); } if ( transf ) { cmsDeleteTransform( transf ); transf = 0; } theOne = cmsOpenProfileFromFile( uri.data(), "r" ); if ( theOne ) { // a display profile must have the proper stuff icColorSpaceSignature space = cmsGetColorSpace(theOne); icProfileClassSignature profClass = cmsGetDeviceClass(theOne); if ( profClass != icSigDisplayClass ) { g_warning("Not a display profile"); cmsCloseProfile( theOne ); theOne = 0; } else if ( space != icSigRgbData ) { g_warning("Not an RGB profile"); cmsCloseProfile( theOne ); theOne = 0; } else { lastURI = uri; } } } } else if ( theOne ) { cmsCloseProfile( theOne ); theOne = 0; lastURI.clear(); if ( transf ) { cmsDeleteTransform( transf ); transf = 0; } } return theOne; } cmsHPROFILE Inkscape::colorprofile_get_proof_profile_handle() { static cmsHPROFILE theOne = 0; static Glib::ustring lastURI; static bool init = false; if ( !init ) { cmsSetErrorHandler(errorHandlerCB); findThings(); init = true; } Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool which = prefs->getBool( "/options/softproof/enable"); Glib::ustring uri = prefs->getString("/options/softproof/uri"); if ( which && !uri.empty() ) { if ( lastURI != uri ) { lastURI.clear(); if ( theOne ) { cmsCloseProfile( theOne ); } if ( transf ) { cmsDeleteTransform( transf ); transf = 0; } theOne = cmsOpenProfileFromFile( uri.data(), "r" ); if ( theOne ) { // a display profile must have the proper stuff icColorSpaceSignature space = cmsGetColorSpace(theOne); icProfileClassSignature profClass = cmsGetDeviceClass(theOne); (void)space; (void)profClass; /* if ( profClass != icSigDisplayClass ) { g_warning("Not a display profile"); cmsCloseProfile( theOne ); theOne = 0; } else if ( space != icSigRgbData ) { g_warning("Not an RGB profile"); cmsCloseProfile( theOne ); theOne = 0; } else { */ lastURI = uri; /* } */ } } } else if ( theOne ) { cmsCloseProfile( theOne ); theOne = 0; lastURI.clear(); if ( transf ) { cmsDeleteTransform( transf ); transf = 0; } } return theOne; } static void free_transforms(); cmsHTRANSFORM Inkscape::colorprofile_get_display_transform() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display"); if ( fromDisplay ) { if ( transf ) { cmsDeleteTransform(transf); transf = 0; } return 0; } bool warn = prefs->getBool( "/options/softproof/gamutwarn"); int intent = prefs->getIntLimited( "/options/displayprofile/intent", 0, 0, 3 ); int proofIntent = prefs->getIntLimited( "/options/softproof/intent", 0, 0, 3 ); bool bpc = prefs->getBool( "/options/softproof/bpc"); #if defined(cmsFLAGS_PRESERVEBLACK) bool preserveBlack = prefs->getBool( "/options/softproof/preserveblack"); #endif //defined(cmsFLAGS_PRESERVEBLACK) Glib::ustring colorStr = prefs->getString("/options/softproof/gamutcolor"); Gdk::Color gamutColor( colorStr.empty() ? "#808080" : colorStr ); if ( (warn != gamutWarn) || (lastIntent != intent) || (lastProofIntent != proofIntent) || (bpc != lastBPC) #if defined(cmsFLAGS_PRESERVEBLACK) || (preserveBlack != lastPreserveBlack) #endif // defined(cmsFLAGS_PRESERVEBLACK) || (gamutColor != lastGamutColor) ) { gamutWarn = warn; free_transforms(); lastIntent = intent; lastProofIntent = proofIntent; lastBPC = bpc; #if defined(cmsFLAGS_PRESERVEBLACK) lastPreserveBlack = preserveBlack; #endif // defined(cmsFLAGS_PRESERVEBLACK) lastGamutColor = gamutColor; } // Fetch these now, as they might clear the transform as a side effect. cmsHPROFILE hprof = Inkscape::colorprofile_get_system_profile_handle(); cmsHPROFILE proofProf = hprof ? Inkscape::colorprofile_get_proof_profile_handle() : 0; if ( !transf ) { if ( hprof && proofProf ) { DWORD dwFlags = cmsFLAGS_SOFTPROOFING; if ( gamutWarn ) { dwFlags |= cmsFLAGS_GAMUTCHECK; cmsSetAlarmCodes(gamutColor.get_red() >> 8, gamutColor.get_green() >> 8, gamutColor.get_blue() >> 8); } if ( bpc ) { dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } #if defined(cmsFLAGS_PRESERVEBLACK) if ( preserveBlack ) { dwFlags |= cmsFLAGS_PRESERVEBLACK; } #endif // defined(cmsFLAGS_PRESERVEBLACK) transf = cmsCreateProofingTransform( ColorProfile::getSRGBProfile(), TYPE_RGBA_8, hprof, TYPE_RGBA_8, proofProf, intent, proofIntent, dwFlags ); } else if ( hprof ) { transf = cmsCreateTransform( ColorProfile::getSRGBProfile(), TYPE_RGBA_8, hprof, TYPE_RGBA_8, intent, 0 ); } } return transf; } class MemProfile { public: MemProfile(); ~MemProfile(); std::string id; cmsHPROFILE hprof; cmsHTRANSFORM transf; }; MemProfile::MemProfile() : id(), hprof(0), transf(0) { } MemProfile::~MemProfile() { } static std::vector< std::vector<MemProfile> > perMonitorProfiles; void free_transforms() { if ( transf ) { cmsDeleteTransform(transf); transf = 0; } for ( std::vector< std::vector<MemProfile> >::iterator it = perMonitorProfiles.begin(); it != perMonitorProfiles.end(); ++it ) { for ( std::vector<MemProfile>::iterator it2 = it->begin(); it2 != it->end(); ++it2 ) { if ( it2->transf ) { cmsDeleteTransform(it2->transf); it2->transf = 0; } } } } Glib::ustring Inkscape::colorprofile_get_display_id( int screen, int monitor ) { Glib::ustring id; if ( screen >= 0 && screen < static_cast<int>(perMonitorProfiles.size()) ) { std::vector<MemProfile>& row = perMonitorProfiles[screen]; if ( monitor >= 0 && monitor < static_cast<int>(row.size()) ) { MemProfile& item = row[monitor]; id = item.id; } } return id; } Glib::ustring Inkscape::colorprofile_set_display_per( gpointer buf, guint bufLen, int screen, int monitor ) { Glib::ustring id; while ( static_cast<int>(perMonitorProfiles.size()) <= screen ) { std::vector<MemProfile> tmp; perMonitorProfiles.push_back(tmp); } std::vector<MemProfile>& row = perMonitorProfiles[screen]; while ( static_cast<int>(row.size()) <= monitor ) { MemProfile tmp; row.push_back(tmp); } MemProfile& item = row[monitor]; if ( item.hprof ) { cmsCloseProfile( item.hprof ); item.hprof = 0; } id.clear(); if ( buf && bufLen ) { id = Digest::hashHex(Digest::HASH_MD5, reinterpret_cast<unsigned char*>(buf), bufLen); // Note: if this is not a valid profile, item.hprof will be set to null. item.hprof = cmsOpenProfileFromMem(buf, bufLen); } item.id = id; return id; } cmsHTRANSFORM Inkscape::colorprofile_get_display_per( Glib::ustring const& id ) { cmsHTRANSFORM result = 0; if ( id.empty() ) { return 0; } Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool found = false; for ( std::vector< std::vector<MemProfile> >::iterator it = perMonitorProfiles.begin(); it != perMonitorProfiles.end() && !found; ++it ) { for ( std::vector<MemProfile>::iterator it2 = it->begin(); it2 != it->end() && !found; ++it2 ) { if ( id == it2->id ) { MemProfile& item = *it2; bool warn = prefs->getBool( "/options/softproof/gamutwarn"); int intent = prefs->getIntLimited( "/options/displayprofile/intent", 0, 0, 3 ); int proofIntent = prefs->getIntLimited( "/options/softproof/intent", 0, 0, 3 ); bool bpc = prefs->getBool( "/options/softproof/bpc"); #if defined(cmsFLAGS_PRESERVEBLACK) bool preserveBlack = prefs->getBool( "/options/softproof/preserveblack"); #endif //defined(cmsFLAGS_PRESERVEBLACK) Glib::ustring colorStr = prefs->getString("/options/softproof/gamutcolor"); Gdk::Color gamutColor( colorStr.empty() ? "#808080" : colorStr ); if ( (warn != gamutWarn) || (lastIntent != intent) || (lastProofIntent != proofIntent) || (bpc != lastBPC) #if defined(cmsFLAGS_PRESERVEBLACK) || (preserveBlack != lastPreserveBlack) #endif // defined(cmsFLAGS_PRESERVEBLACK) || (gamutColor != lastGamutColor) ) { gamutWarn = warn; free_transforms(); lastIntent = intent; lastProofIntent = proofIntent; lastBPC = bpc; #if defined(cmsFLAGS_PRESERVEBLACK) lastPreserveBlack = preserveBlack; #endif // defined(cmsFLAGS_PRESERVEBLACK) lastGamutColor = gamutColor; } // Fetch these now, as they might clear the transform as a side effect. cmsHPROFILE proofProf = item.hprof ? Inkscape::colorprofile_get_proof_profile_handle() : 0; if ( !item.transf ) { if ( item.hprof && proofProf ) { DWORD dwFlags = cmsFLAGS_SOFTPROOFING; if ( gamutWarn ) { dwFlags |= cmsFLAGS_GAMUTCHECK; cmsSetAlarmCodes(gamutColor.get_red() >> 8, gamutColor.get_green() >> 8, gamutColor.get_blue() >> 8); } if ( bpc ) { dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; } #if defined(cmsFLAGS_PRESERVEBLACK) if ( preserveBlack ) { dwFlags |= cmsFLAGS_PRESERVEBLACK; } #endif // defined(cmsFLAGS_PRESERVEBLACK) item.transf = cmsCreateProofingTransform( ColorProfile::getSRGBProfile(), TYPE_RGBA_8, item.hprof, TYPE_RGBA_8, proofProf, intent, proofIntent, dwFlags ); } else if ( item.hprof ) { item.transf = cmsCreateTransform( ColorProfile::getSRGBProfile(), TYPE_RGBA_8, item.hprof, TYPE_RGBA_8, intent, 0 ); } } result = item.transf; found = true; } } } return result; } #endif // ENABLE_LCMS /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :