From da6a8153c94f9e26efdd30f8866dcd091add7202 Mon Sep 17 00:00:00 2001 From: joncruz Date: Sat, 24 Nov 2007 09:41:35 +0000 Subject: [PATCH] Implemented per-monitor display adjustment via XICC --- src/color-profile-fns.h | 4 + src/color-profile.cpp | 207 +++++++++++++++++++++++-- src/display/sp-canvas.cpp | 8 +- src/ege-color-prof-tracker.cpp | 122 +++++++++++++-- src/ege-color-prof-tracker.h | 1 + src/preferences-skeleton.h | 1 + src/ui/dialog/inkscape-preferences.cpp | 12 +- src/ui/dialog/inkscape-preferences.h | 1 + src/widgets/desktop-widget.cpp | 59 +++++-- 9 files changed, 373 insertions(+), 42 deletions(-) diff --git a/src/color-profile-fns.h b/src/color-profile-fns.h index 24044ddce..c8c51b551 100644 --- a/src/color-profile-fns.h +++ b/src/color-profile-fns.h @@ -30,6 +30,10 @@ GType colorprofile_get_type(); cmsHPROFILE colorprofile_get_handle( SPDocument* document, guint* intent, gchar const* name ); cmsHTRANSFORM colorprofile_get_display_transform(); +Glib::ustring colorprofile_get_display_id( int screen, int monitor ); +Glib::ustring colorprofile_set_display_per( gpointer buf, guint bufLen, int screen, int monitor ); +cmsHTRANSFORM colorprofile_get_display_per( Glib::ustring const& id ); + std::vector colorprofile_get_display_names(); std::vector colorprofile_get_softproof_names(); diff --git a/src/color-profile.cpp b/src/color-profile.cpp index 2a17f698b..a95039928 100644 --- a/src/color-profile.cpp +++ b/src/color-profile.cpp @@ -1,15 +1,5 @@ -#include "xml/repr.h" -#include "color-profile.h" -#include "color-profile-fns.h" -#include "attributes.h" -#include "inkscape.h" -#include "document.h" -#include "prefs-utils.h" - -#include "dom/uri.h" - //#define DEBUG_LCMS #include @@ -20,6 +10,17 @@ #include #endif // DEBUG_LCMS +#include "xml/repr.h" +#include "color-profile.h" +#include "color-profile-fns.h" +#include "attributes.h" +#include "inkscape.h" +#include "document.h" +#include "prefs-utils.h" + +#include "dom/uri.h" +#include "dom/util/digest.h" + using Inkscape::ColorProfile; using Inkscape::ColorProfileClass; @@ -789,8 +790,19 @@ cmsHPROFILE Inkscape::colorprofile_get_proof_profile_handle() return theOne; } +static void free_transforms(); + cmsHTRANSFORM Inkscape::colorprofile_get_display_transform() { + long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 ); + if ( fromDisplay ) { + if ( transf ) { + cmsDeleteTransform(transf); + transf = 0; + } + return 0; + } + bool warn = prefs_get_int_attribute_limited( "options.softproof", "gamutwarn", 0, 0, 1 ); int intent = prefs_get_int_attribute_limited( "options.displayprofile", "intent", 0, 0, 3 ); int proofIntent = prefs_get_int_attribute_limited( "options.softproof", "intent", 0, 0, 3 ); @@ -811,10 +823,7 @@ cmsHTRANSFORM Inkscape::colorprofile_get_display_transform() || (gamutColor != lastGamutColor) ) { gamutWarn = warn; - if ( transf ) { - cmsDeleteTransform(transf); - transf = 0; - } + free_transforms(); lastIntent = intent; lastProofIntent = proofIntent; lastBPC = bpc; @@ -852,6 +861,176 @@ cmsHTRANSFORM Inkscape::colorprofile_get_display_transform() 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 ) { + Md5Digest digest; + if ( buf && bufLen ) { + digest.append(reinterpret_cast(buf), bufLen); + } + id = digest.finishHex(); + + // 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; + } + + 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_get_int_attribute_limited( "options.softproof", "gamutwarn", 0, 0, 1 ); + int intent = prefs_get_int_attribute_limited( "options.displayprofile", "intent", 0, 0, 3 ); + int proofIntent = prefs_get_int_attribute_limited( "options.softproof", "intent", 0, 0, 3 ); + bool bpc = prefs_get_int_attribute_limited( "options.softproof", "bpc", 0, 0, 1 ); +#if defined(cmsFLAGS_PRESERVEBLACK) + bool preserveBlack = prefs_get_int_attribute_limited( "options.softproof", "preserveblack", 0, 0, 1 ); +#endif //defined(cmsFLAGS_PRESERVEBLACK) + gchar const* colorStr = prefs_get_string_attribute("options.softproof", "gamutcolor"); + Gdk::Color gamutColor( (colorStr && colorStr[0]) ? colorStr : "#808080"); + + 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_RGB_8, item.hprof, TYPE_RGB_8, proofProf, intent, proofIntent, dwFlags ); + } else if ( item.hprof ) { + item.transf = cmsCreateTransform( ColorProfile::getSRGBProfile(), TYPE_RGB_8, item.hprof, TYPE_RGB_8, intent, 0 ); + } + } + + result = item.transf; + found = true; + } + } + } + + return result; +} + + + #endif // ENABLE_LCMS /* diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 80b9103eb..2af12b6c3 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -1593,7 +1593,13 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, } #if ENABLE_LCMS - cmsHTRANSFORM transf = Inkscape::colorprofile_get_display_transform(); + cmsHTRANSFORM transf = 0; + long long int fromDisplay = prefs_get_int_attribute_limited( "options.displayprofile", "from_display", 0, 0, 1 ); + if ( fromDisplay ) { + transf = Inkscape::colorprofile_get_display_per( canvas->cms_key ? *(canvas->cms_key) : "" ); + } else { + transf = Inkscape::colorprofile_get_display_transform(); + } #endif // ENABLE_LCMS if (buf.is_empty) { diff --git a/src/ege-color-prof-tracker.cpp b/src/ege-color-prof-tracker.cpp index 2496b8dd6..7e51db933 100644 --- a/src/ege-color-prof-tracker.cpp +++ b/src/ege-color-prof-tracker.cpp @@ -43,6 +43,7 @@ #include #include +#include #ifdef GDK_WINDOWING_X11 #include @@ -87,6 +88,9 @@ const gchar* gdk_x11_get_xatom_name_for_display (GdkDisplay *display, enum { CHANGED = 0, + ADDED, + REMOVED, + MODIFIED, LAST_SIGNAL}; @@ -115,7 +119,7 @@ static GObjectClass* gParentClass = 0; static guint signals[LAST_SIGNAL] = {0}; static GSList* tracked_screens = 0; - +static GSList* abstract_trackers = 0; struct _EgeColorProfTrackerPrivate { @@ -135,6 +139,7 @@ static void screen_size_changed_cb(GdkScreen* screen, gpointer user_data); static void fire(GdkScreen* screen, gint monitor); static void clear_profile( GdkScreen* screen, guint monitor ); static void set_profile( GdkScreen* screen, guint monitor, const guint8* data, guint len ); +static void track_screen( GdkScreen* screen, EgeColorProfTracker* tracker ); GType ege_color_prof_tracker_get_type( void ) { @@ -167,6 +172,7 @@ void ege_color_prof_tracker_class_init( EgeColorProfTrackerClass* klass ) objClass->get_property = ege_color_prof_tracker_get_property; objClass->set_property = ege_color_prof_tracker_set_property; + klass->changed = 0; signals[CHANGED] = g_signal_new( "changed", G_TYPE_FROM_CLASS(klass), @@ -174,7 +180,37 @@ void ege_color_prof_tracker_class_init( EgeColorProfTrackerClass* klass ) G_STRUCT_OFFSET(EgeColorProfTrackerClass, changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + G_TYPE_NONE, 0 ); + + signals[ADDED] = g_signal_new( "added", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + NULL, + NULL, NULL, + gtk_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_INT); + + signals[REMOVED] = g_signal_new( "removed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + NULL, + NULL, NULL, + gtk_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_INT); + + signals[MODIFIED] = g_signal_new( "modified", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_FIRST, + NULL, + NULL, NULL, + gtk_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_INT); g_type_class_add_private( klass, sizeof(EgeColorProfTrackerClass) ); } @@ -196,13 +232,30 @@ EgeColorProfTracker* ege_color_prof_tracker_new( GtkWidget* target ) EgeColorProfTracker* tracker = EGE_COLOR_PROF_TRACKER( obj ); tracker->private_data->_target = target; - g_object_weak_ref( G_OBJECT(target), target_finalized, obj ); - g_signal_connect( G_OBJECT(target), "hierarchy-changed", G_CALLBACK( target_hierarchy_changed_cb ), obj ); - g_signal_connect( G_OBJECT(target), "screen-changed", G_CALLBACK( target_screen_changed_cb ), obj ); + if ( target ) { + g_object_weak_ref( G_OBJECT(target), target_finalized, obj ); + g_signal_connect( G_OBJECT(target), "hierarchy-changed", G_CALLBACK( target_hierarchy_changed_cb ), obj ); + g_signal_connect( G_OBJECT(target), "screen-changed", G_CALLBACK( target_screen_changed_cb ), obj ); - /* invoke the callbacks now to connect if the widget is already visible */ - target_hierarchy_changed_cb( target, 0, obj ); - target_screen_changed_cb( target, 0, obj ); + /* invoke the callbacks now to connect if the widget is already visible */ + target_hierarchy_changed_cb( target, 0, obj ); + target_screen_changed_cb( target, 0, obj ); + } else { + abstract_trackers = g_slist_append( abstract_trackers, obj ); + + GSList* curr = tracked_screens; + while ( curr ) { + ScreenTrack* track = (ScreenTrack*)curr->data; + gint screenNum = gdk_screen_get_number(track->screen); + gint monitor = 0; + for ( monitor = 0; monitor < (gint)track->profiles->len; monitor++ ) { + g_signal_emit( G_OBJECT(tracker), signals[MODIFIED], 0, screenNum, monitor ); + } + + curr = g_slist_next(curr); + } + + } return tracker; } @@ -240,6 +293,43 @@ void ege_color_prof_tracker_get_profile( EgeColorProfTracker const * tracker, gp } } +void ege_color_prof_tracker_get_profile_for( guint screenNum, guint monitor, gpointer* ptr, guint* len ) +{ + gpointer dataPos = 0; + guint dataLen = 0; + GdkDisplay* display = gdk_display_get_default(); + gint numScreens = gdk_display_get_n_screens(display); + GdkScreen* screen = (screenNum < (guint)numScreens) ? gdk_display_get_screen(display, screenNum) : 0; + + if ( screen ) { + GSList* curr = tracked_screens; + while ( curr ) { + ScreenTrack* screenTrack = (ScreenTrack*)curr->data; + if ( screenTrack->screen == screen ) { + if ( monitor < screenTrack->profiles->len ) { + GByteArray* gba = (GByteArray*)g_ptr_array_index( screenTrack->profiles, monitor ); + if ( gba ) { + dataPos = gba->data; + dataLen = gba->len; + } + } else { + g_warning("No profile data tracked for the specified item."); + } + break; + } + + curr = g_slist_next(curr); + } + } + + if ( ptr ) { + *ptr = dataPos; + } + if ( len ) { + *len = dataLen; + } +} + void ege_color_prof_tracker_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec ) { EgeColorProfTracker* tracker = EGE_COLOR_PROF_TRACKER( obj ); @@ -264,7 +354,7 @@ void ege_color_prof_tracker_set_property( GObject* obj, guint propId, const GVal } -static void track_screen( GdkScreen* screen, EgeColorProfTracker* tracker ) +void track_screen( GdkScreen* screen, EgeColorProfTracker* tracker ) { GSList* curr = tracked_screens; /* First remove the tracker from different screens */ @@ -457,9 +547,13 @@ static void set_profile( GdkScreen* screen, guint monitor, const guint8* data, g curr = g_slist_next(curr); } if ( curr ) { + /* Something happened to a screen being tracked. */ ScreenTrack* track = (ScreenTrack*)curr->data; + gint screenNum = gdk_screen_get_number(screen); guint i = 0; GByteArray* previous = 0; + GSList* abstracts = 0; + for ( i = track->profiles->len; i <= monitor; i++ ) { g_ptr_array_add( track->profiles, 0 ); } @@ -475,6 +569,10 @@ static void set_profile( GdkScreen* screen, guint monitor, const guint8* data, g } else { track->profiles->pdata[monitor] = 0; } + + for ( abstracts = abstract_trackers; abstracts; abstracts = g_slist_next(abstracts) ) { + g_signal_emit( G_OBJECT(abstracts->data), signals[MODIFIED], 0, screenNum, monitor ); + } } } @@ -548,6 +646,10 @@ void handle_property_change(GdkScreen* screen, const gchar* name) size = nitems + bytesAfter; bytesAfter = 0; nitems = 0; + if ( prop ) { + XFree(prop); + prop = 0; + } if ( XGetWindowProperty( xdisplay, GDK_WINDOW_XID(gdk_screen_get_root_window(screen)), atom, 0, size, False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesAfter, &prop ) == Success ) { @@ -562,7 +664,7 @@ void handle_property_change(GdkScreen* screen, const gchar* name) set_profile( screen, monitor, 0, 0 ); } } else { - g_message("error loading profile property"); + g_warning("error loading profile property"); } } fire(screen, monitor); diff --git a/src/ege-color-prof-tracker.h b/src/ege-color-prof-tracker.h index 69a3112fb..09aeae970 100644 --- a/src/ege-color-prof-tracker.h +++ b/src/ege-color-prof-tracker.h @@ -98,6 +98,7 @@ GType ege_color_prof_tracker_get_type( void ); EgeColorProfTracker* ege_color_prof_tracker_new( GtkWidget* target ); void ege_color_prof_tracker_get_profile( EgeColorProfTracker const * tracker, gpointer* ptr, guint* len ); +void ege_color_prof_tracker_get_profile_for( guint screen, guint monitor, gpointer* ptr, guint* len ); G_END_DECLS diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index ca9988494..d73819746 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -184,6 +184,7 @@ static char const preferences_skeleton[] = " \n" " dtws; + EgeColorProfTracker *_tracker; }; +PrefWatcher::PrefWatcher() : + NodeObserver(), + dtws(), + _tracker(0) +{ + _tracker = ege_color_prof_tracker_new(0); + g_signal_connect( G_OBJECT(_tracker), "modified", G_CALLBACK(hook), this ); +} + PrefWatcher::~PrefWatcher() { } +void PrefWatcher::hook(EgeColorProfTracker */*tracker*/, gint screen, gint monitor, PrefWatcher */*watcher*/) +{ + unsigned char* buf = 0; + guint len = 0; + + ege_color_prof_tracker_get_profile_for( screen, monitor, reinterpret_cast(&buf), &len ); + Glib::ustring id = Inkscape::colorprofile_set_display_per( buf, len, screen, monitor ); +} + void PrefWatcher::add( SPDesktopWidget* dtw ) { dtws.push_back(dtw); @@ -501,6 +517,15 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) gtk_box_pack_start(GTK_BOX(dtw->statusbar), GTK_WIDGET(dtw->layer_selector->gobj()), FALSE, FALSE, 1); dtw->_tracker = ege_color_prof_tracker_new(GTK_WIDGET(dtw->layer_selector->gobj())); + { + Glib::ustring id = Inkscape::colorprofile_get_display_id( 0, 0 ); + bool enabled = false; + if ( dtw->canvas->cms_key ) { + *(dtw->canvas->cms_key) = id; + enabled = !dtw->canvas->cms_key->empty(); + } + gtk_widget_set_sensitive( dtw->cms_adjust, enabled ); + } g_signal_connect( G_OBJECT(dtw->_tracker), "changed", G_CALLBACK(sp_dtw_color_profile_event), dtw ); dtw->select_status_eventbox = gtk_event_box_new (); @@ -707,18 +732,20 @@ sp_desktop_widget_event (GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dt return FALSE; } -void sp_dtw_color_profile_event(EgeColorProfTracker *tracker, SPDesktopWidget */*dtw*/) +void sp_dtw_color_profile_event(EgeColorProfTracker */*tracker*/, SPDesktopWidget *dtw) { // Handle profile changes - Md5Digest digest; - unsigned char* buf = 0; - guint len = 0; - ege_color_prof_tracker_get_profile( tracker, reinterpret_cast(&buf), &len ); - if ( buf && len ) { - digest.append(buf, len); + GdkScreen* screen = gtk_widget_get_screen(GTK_WIDGET(dtw)); + gint screenNum = gdk_screen_get_number(screen); + gint monitor = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_toplevel(GTK_WIDGET(dtw))->window); + Glib::ustring id = Inkscape::colorprofile_get_display_id( screenNum, monitor ); + bool enabled = false; + if ( dtw->canvas->cms_key ) { + *(dtw->canvas->cms_key) = id; + dtw->requestCanvasUpdate(); + enabled = !dtw->canvas->cms_key->empty(); } - std::string hash = digest.finishHex(); - //g_message("ICC profile %d bytes at %p is [%s]", len, buf, hash.c_str() ); + gtk_widget_set_sensitive( dtw->cms_adjust, enabled ); } void cms_adjust_toggled( GtkWidget */*button*/, gpointer data ) -- 2.30.2