X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fcolor-profile.cpp;h=e08a416d3b829a9ac84379956186d5c5166ffb6a;hb=0dc33d4ce43e0bb49c63aa53b826ec4a1ff68e28;hp=5c5303e08355a8e515e541a0a453d289da598122;hpb=1dec520cc2d39cea31661d4a8fa1cad67facb255;p=inkscape.git diff --git a/src/color-profile.cpp b/src/color-profile.cpp index 5c5303e08..e08a416d3 100644 --- a/src/color-profile.cpp +++ b/src/color-profile.cpp @@ -1,42 +1,64 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#define noDEBUG_LCMS + +#include +#include +#include +#include + +#ifdef DEBUG_LCMS +#include +#endif // DEBUG_LCMS + +#include +#include +#include + +#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 +#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 "prefs-utils.h" +#include "preferences.h" #include "dom/uri.h" +#include "dom/util/digest.h" -//#define DEBUG_LCMS - -#ifdef DEBUG_LCMS -#include -#endif // DEBUG_LCMS +#ifdef WIN32 +#include +#endif // WIN32 using Inkscape::ColorProfile; using Inkscape::ColorProfileClass; namespace Inkscape { -static void colorprofile_class_init( ColorProfileClass *klass ); -static void colorprofile_init( ColorProfile *cprof ); - -static void colorprofile_release( SPObject *object ); -static void colorprofile_build( SPObject *object, SPDocument *document, Inkscape::XML::Node *repr ); -static void colorprofile_set( SPObject *object, unsigned key, gchar const *value ); -static Inkscape::XML::Node *colorprofile_write( SPObject *object, Inkscape::XML::Node *repr, guint flags ); +#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(key, ...) \ +#define DEBUG_MESSAGE_SCISLAC(key, ...) \ {\ - gint dump = prefs_get_int_attribute_limited("options.scislac", #key, 0, 0, 1);\ - gint dumpD = prefs_get_int_attribute_limited("options.scislac", #key"D", 0, 0, 1);\ - gint dumpD2 = prefs_get_int_attribute_limited("options.scislac", #key"D2", 0, 0, 1);\ + 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 )\ {\ @@ -57,25 +79,59 @@ extern guint update_in_progress; gtk_widget_show_all( dialog );\ }\ } + + +#define DEBUG_MESSAGE(key, ...)\ +{\ + g_message( __VA_ARGS__ );\ +} + #endif // DEBUG_LCMS -static SPObject *cprof_parent_class; +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. */ 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_class_init, + (GClassInitFunc) ColorProfile::classInit, NULL, NULL, sizeof(ColorProfile), 16, - (GInstanceInitFunc) colorprofile_init, + (GInstanceInitFunc) ColorProfile::init, NULL, /* value_table */ }; type = g_type_register_static( SP_TYPE_OBJECT, "ColorProfile", &info, static_cast(0) ); @@ -86,22 +142,22 @@ GType Inkscape::colorprofile_get_type() /** * ColorProfile vtable initialization. */ -static void Inkscape::colorprofile_class_init( ColorProfileClass *klass ) +void ColorProfile::classInit( ColorProfileClass *klass ) { SPObjectClass *sp_object_class = reinterpret_cast(klass); - cprof_parent_class = static_cast(g_type_class_ref(SP_TYPE_OBJECT)); + cprof_parent_class = static_cast(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; + 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. */ -static void Inkscape::colorprofile_init( ColorProfile *cprof ) +void ColorProfile::init( ColorProfile *cprof ) { cprof->href = 0; cprof->local = 0; @@ -110,14 +166,24 @@ static void Inkscape::colorprofile_init( ColorProfile *cprof ) 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 */ -static void Inkscape::colorprofile_release( SPObject *object ) +void ColorProfile::release( SPObject *object ) { + // Unregister ourselves + if ( object->document ) { + object->document->removeResource("iccprofile", object); + } + ColorProfile *cprof = COLORPROFILE(object); if ( cprof->href ) { g_free( cprof->href ); @@ -140,17 +206,38 @@ static void Inkscape::colorprofile_release( SPObject *object ) } #if ENABLE_LCMS - if ( cprof->profHandle ) { - cmsCloseProfile( cprof->profHandle ); - cprof->profHandle = 0; - } + 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. */ -static void Inkscape::colorprofile_build( SPObject *object, SPDocument *document, Inkscape::XML::Node *repr ) +void ColorProfile::build( SPObject *object, SPDocument *document, Inkscape::XML::Node *repr ) { ColorProfile *cprof = COLORPROFILE(object); g_assert(cprof->href == 0); @@ -158,19 +245,24 @@ static void Inkscape::colorprofile_build( SPObject *object, SPDocument *document g_assert(cprof->name == 0); g_assert(cprof->intentStr == 0); - if (((SPObjectClass *) cprof_parent_class)->build) { - (* ((SPObjectClass *) cprof_parent_class)->build)(object, document, repr); + if (cprof_parent_class->build) { + (* cprof_parent_class->build)(object, document, repr); + } + object->readAttr( "xlink:href" ); + object->readAttr( "local" ); + object->readAttr( "name" ); + object->readAttr( "rendering-intent" ); + + // Register + if ( document ) { + document->addResource( "iccprofile", object ); } - 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" ); } /** * Callback: set attribute. */ -static void Inkscape::colorprofile_set( SPObject *object, unsigned key, gchar const *value ) +void ColorProfile::set( SPObject *object, unsigned key, gchar const *value ) { ColorProfile *cprof = COLORPROFILE(object); @@ -197,25 +289,35 @@ static void Inkscape::colorprofile_set( SPObject *object, unsigned key, gchar co g_warning("object has no document. using active"); } //# 1. Get complete URI of document - gchar const *docbase = SP_DOCUMENT_URI( doc ); + gchar const *docbase = doc->getURI(); if (!docbase) - { - g_warning("null 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(cprof->href); + 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 = (gchar *)cprofUri.getNativePath().c_str(); + 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 } } @@ -272,8 +374,8 @@ static void Inkscape::colorprofile_set( SPObject *object, unsigned key, gchar co break; default: - if (((SPObjectClass *) cprof_parent_class)->set) { - (* ((SPObjectClass *) cprof_parent_class)->set)(object, key, value); + if (cprof_parent_class->set) { + (* cprof_parent_class->set)(object, key, value); } break; } @@ -283,12 +385,11 @@ static void Inkscape::colorprofile_set( SPObject *object, unsigned key, gchar co /** * Callback: write attributes to associated repr. */ -static Inkscape::XML::Node* Inkscape::colorprofile_write( SPObject *object, Inkscape::XML::Node *repr, guint flags ) +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) { - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object)); repr = xml_doc->createElement("svg:color-profile"); } @@ -308,8 +409,8 @@ static Inkscape::XML::Node* Inkscape::colorprofile_write( SPObject *object, Inks repr->setAttribute( "rendering-intent", cprof->intentStr ); } - if (((SPObjectClass *) cprof_parent_class)->write) { - (* ((SPObjectClass *) cprof_parent_class)->write)(object, repr, flags); + if (cprof_parent_class->write) { + (* cprof_parent_class->write)(object, xml_doc, repr, flags); } return repr; @@ -318,30 +419,75 @@ static Inkscape::XML::Node* Inkscape::colorprofile_write( SPObject *object, Inks #if ENABLE_LCMS +struct MapMap { + icColorSpaceSignature space; + DWORD inForm; +}; -static SPObject* bruteFind( SPObject* curr, gchar const* name ) +DWORD ColorProfile::_getInputFormat( icColorSpaceSignature space ) { - SPObject* result = 0; + 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; + } + } - if ( curr ) { - if ( IS_COLORPROFILE(curr) ) { - ColorProfile* prof = COLORPROFILE(curr); + 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 = document->getResourceList("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 = curr; + result = SP_OBJECT(current->data); + break; } } - } else { - if ( curr->hasChildren() ) { - SPObject* child = curr->firstChild(); - while ( child && !result ) { - result = bruteFind( child, name ); - if ( !result ) { - child = child->next; - } - }; - } } + current = g_slist_next(current); } return result; @@ -351,8 +497,7 @@ cmsHPROFILE Inkscape::colorprofile_get_handle( SPDocument* document, guint* inte { cmsHPROFILE prof = 0; - SPObject* root = SP_DOCUMENT_ROOT(document); - SPObject* thing = bruteFind( root, name ); + SPObject* thing = bruteFind( document, name ); if ( thing ) { prof = COLORPROFILE(thing)->profHandle; } @@ -368,35 +513,673 @@ cmsHPROFILE Inkscape::colorprofile_get_handle( SPDocument* document, guint* inte 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); +} + +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 knownProfiles; + +std::vector Inkscape::colorprofile_get_display_names() +{ + std::vector result; + + for ( std::vector::iterator it = knownProfiles.begin(); it != knownProfiles.end(); ++it ) { + if ( it->getClass() == icSigDisplayClass && it->getSpace() == icSigRgbData ) { + result.push_back( it->getName() ); + } + } + + return result; +} + +std::vector Inkscape::colorprofile_get_softproof_names() +{ + std::vector result; + + for ( std::vector::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::iterator it = knownProfiles.begin(); it != knownProfiles.end(); ++it ) { + if ( name == it->getName() ) { + result = it->getPath(); + break; + } + } + + return result; +} +#endif // ENABLE_LCMS + +std::list ColorProfile::getBaseProfileDirs() { +#if ENABLE_LCMS + static bool warnSet = false; + if (!warnSet) { + cmsErrorAction( LCMS_ERROR_SHOW ); + warnSet = true; + } +#endif // ENABLE_LCMS + std::list 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 possible; + possible.push_back("/System/Library/ColorSync/Profiles"); + possible.push_back("/Library/ColorSync/Profiles"); + for ( std::list::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(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 ColorProfile::getProfileFiles() +{ + std::list files; + + std::list sources = ColorProfile::getBaseProfileDirs(); + for ( std::list::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 files = ColorProfile::getProfileFiles(); + + for ( std::list::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::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 std::string lastURI; + static Glib::ustring lastURI; - long long int which = prefs_get_int_attribute_limited( "options.displayprofile", "enable", 0, 0, 1 ); - gchar const * uri = prefs_get_string_attribute("options.displayprofile", "uri"); + static bool init = false; + if ( !init ) { + cmsSetErrorHandler(errorHandlerCB); + + findThings(); + init = true; + } - if ( which && uri ) { - if ( lastURI != std::string(uri) ) { + 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 ); } - theOne = cmsOpenProfileFromFile( uri, "r" ); + if ( transf ) { + cmsDeleteTransform( transf ); + transf = 0; + } + theOne = cmsOpenProfileFromFile( uri.data(), "r" ); if ( theOne ) { - lastURI = uri; + // 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 > perMonitorProfiles; + +void free_transforms() +{ + if ( transf ) { + cmsDeleteTransform(transf); + transf = 0; + } + + for ( std::vector< std::vector >::iterator it = perMonitorProfiles.begin(); it != perMonitorProfiles.end(); ++it ) { + for ( std::vector::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(perMonitorProfiles.size()) ) { + std::vector& row = perMonitorProfiles[screen]; + if ( monitor >= 0 && monitor < static_cast(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(perMonitorProfiles.size()) <= screen ) { + std::vector tmp; + perMonitorProfiles.push_back(tmp); + } + std::vector& row = perMonitorProfiles[screen]; + while ( static_cast(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(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 >::iterator it = perMonitorProfiles.begin(); it != perMonitorProfiles.end() && !found; ++it ) { + for ( std::vector::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 /* @@ -408,4 +1191,4 @@ cmsHPROFILE Inkscape::colorprofile_get_system_profile_handle() fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :