1 // -*- c-basic-offset: 4 -*-
2 
3 /** @file wxcms.cpp
4  *
5  *  @brief implementation of helper function for color managment
6  *
7  *  @author T. Modes
8  */
9 
10 /*  This program is free software; you can redistribute it and/or
11  *  modify it under the terms of the GNU General Public
12  *  License as published by the Free Software Foundation; either
13  *  version 2 of the License, or (at your option) any later version.
14  *
15  *  This software is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  *  General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public
21  *  License along with this software. If not, see
22  *  <http://www.gnu.org/licenses/>.
23  *
24  */
25 
26 #include "wxcms.h"
27 #include "hugin_utils/utils.h"
28 #ifdef __WXGTK__
29 #include <X11/Xlib.h>
30 #endif
31 #ifdef __WXMAC__
32 #include <ApplicationServices/ApplicationServices.h>
33 #include <CoreFoundation/CoreFoundation.h>
34 #include "wx/osx/core/cfstring.h"
35 #include <wx/osx/private.h>
36 #endif
37 
38 namespace HuginBase
39 {
40     namespace Color
41     {
42         namespace detail
43         {
44 #ifdef __WXMSW__
45             // retrieve monitor profile from Windows
46             // TODO: support for multi-monitor setups
GetMonitorProfile(wxString & profileName,cmsHPROFILE & profile)47             void GetMonitorProfile(wxString& profileName, cmsHPROFILE& profile)
48             {
49                 // look up monitor profile in system
50                 HDC hdc = GetDC(NULL);
51                 if (hdc)
52                 {
53                     wxChar filename[MAX_PATH];
54                     DWORD len;
55                     if (GetICMProfile(hdc, &len, filename))
56                     {
57                         profileName = filename;
58                         profile = cmsOpenProfileFromFile(profileName.c_str(), "r");
59                     };
60                     ReleaseDC(NULL, hdc);
61                 };
62             };
63 #elif defined __WXGTK__
64             cmsHPROFILE GetProfileFromAtom(Display* disp, const char* prop_name)
65             {
66                 Atom atom = XInternAtom(disp, prop_name, True);
67                 if (atom)
68                 {
69                     int actual_format_return;
70                     unsigned long nitems_return = 0;
71                     unsigned long bytes_after_return = 0;
72                     unsigned char* prop_return = 0;
73                     Atom a;
74                     Window w = XDefaultRootWindow(disp);
75                     if (w)
76                     {
77                         XGetWindowProperty(disp, w, atom, 0, INT_MAX, False,
78                             AnyPropertyType,
79                             &a, &actual_format_return, &nitems_return,
80                             &bytes_after_return, &prop_return);
81                         if (nitems_return && prop_return)
82                         {
83                             cmsHPROFILE profile = cmsOpenProfileFromMem(prop_return, nitems_return);
84                             XFree(prop_return);
85                             if (profile != NULL)
86                             {
87                                 return profile;
88                             };
89                         };
90                     };
91                 };
92                 return NULL;
93             }
94 
95             // retrieve monitor profile from X system
96             // TODO: support for multi-monitor setups
97             void GetMonitorProfile(wxString& profileName, cmsHPROFILE& profile)
98             {
99                 Display *disp = XOpenDisplay(0);
100                 if (disp)
101                 {
102                     // when using libXcm we should also use
103                     // profile = GetProfileFromAtom(disp, "_ICC_DEVICE_PROFILE");
104                     // profile = GetProfileFromAtom(disp, XCM_ICC_COLOUR_SERVER_TARGET_PROFILE_IN_X_BASE)
105                     // but in this case we need to update the code to take X11/Xcm/Xcm.h XcolorRegion
106                     // into account
107                     profile = GetProfileFromAtom(disp, "_ICC_PROFILE");
108                     if (profile != NULL)
109                     {
110                         profileName = wxString(hugin_utils::GetICCDesc(profile).c_str(), wxConvLocal);
111                     }
112                     XSync(disp, False);
113                     XCloseDisplay(disp);
114                 };
115             };
116 #elif defined __WXMAC__
117             // retrieve monitor profile for Mac
118             typedef struct {
119                 CFUUIDRef dispuuid;
120                 CFURLRef url;
121             } ColorsyncIteratorData;
122 
123             static bool ColorSyncIterateCallback(CFDictionaryRef dict, void *data)
124             {
125                 ColorsyncIteratorData *iterData = (ColorsyncIteratorData *)data;
126                 CFStringRef str;
127                 CFUUIDRef uuid;
128                 CFBooleanRef iscur;
129 
130                 if (!CFDictionaryGetValueIfPresent(dict, kColorSyncDeviceClass, (const void**)&str))
131                 {
132                     DEBUG_INFO("kColorSyncDeviceClass failed");
133                     return true;
134                 }
135                 if (!CFEqual(str, kColorSyncDisplayDeviceClass))
136                 {
137                     return true;
138                 }
139                 if (!CFDictionaryGetValueIfPresent(dict, kColorSyncDeviceID, (const void**)&uuid))
140                 {
141                     DEBUG_INFO("kColorSyncDeviceID failed");
142                     return true;
143                 }
144                 if (!CFEqual(uuid, iterData->dispuuid))
145                 {
146                     return true;
147                 }
148                 if (!CFDictionaryGetValueIfPresent(dict, kColorSyncDeviceProfileIsCurrent, (const void**)&iscur))
149                 {
150                     DEBUG_INFO("kColorSyncDeviceProfileIsCurrent failed");
151                     return true;
152                 }
153                 if (!CFBooleanGetValue(iscur))
154                 {
155                     return true;
156                 }
157                 if (!CFDictionaryGetValueIfPresent(dict, kColorSyncDeviceProfileURL, (const void**)&(iterData->url)))
158                 {
159                     DEBUG_INFO("Could not get current profile URL");
160                     return true;
161                 }
162                 CFRetain(iterData->url);
163                 return false;
164             }
165 
166             void GetMonitorProfile(wxString& profileName, cmsHPROFILE& profile)
167             {
168                 ColorsyncIteratorData data;
169                 data.dispuuid = CGDisplayCreateUUIDFromDisplayID(CGMainDisplayID());
170                 if (data.dispuuid == NULL)
171                 {
172                     DEBUG_INFO("CGDisplayCreateUUIDFromDisplayID() failed.");
173                     return;
174                 }
175                 data.url = NULL;
176                 ColorSyncIterateDeviceProfiles(ColorSyncIterateCallback, (void *)&data);
177                 CFRelease(data.dispuuid);
178 
179                 CFStringRef urlstr = CFURLCopyFileSystemPath(data.url, kCFURLPOSIXPathStyle);
180                 CFRelease(data.url);
181                 if (urlstr == NULL)
182                 {
183                     DEBUG_INFO("Failed to get URL in CFString");
184                 }
185                 else
186                 {
187                     CFRetain(urlstr);
188                     profileName = wxCFStringRef(urlstr).AsString(wxLocale::GetSystemEncoding());
189                     profile = cmsOpenProfileFromFile(profileName.c_str(), "r");
190                     DEBUG_INFO("Found profile: " << profileName.c_str());
191                 };
192             };
193 #else
194             // general case, does nothing
195             void GetMonitorProfile(wxString& profileName, cmsHPROFILE& profile)
196             {
197             };
198 #endif
199         }
200 
GetMonitorProfile(wxString & profileName,cmsHPROFILE & profile)201         void GetMonitorProfile(wxString& profileName, cmsHPROFILE& profile)
202         {
203             if (profile != NULL)
204             {
205                 cmsCloseProfile(profile);
206             }
207             profileName.Clear();
208             profile = NULL;
209             detail::GetMonitorProfile(profileName, profile);
210             // check if monitor profile could be successful loaded, if not switch back to default sRGB profile
211             if (profile == NULL)
212             {
213                 profile = cmsCreate_sRGBProfile();
214                 profileName.Clear();
215             };
216         };
217 
CorrectImage(wxImage & image,const vigra::ImageImportInfo::ICCProfile & iccProfile,const cmsHPROFILE & monitorProfile)218         void CorrectImage(wxImage& image, const vigra::ImageImportInfo::ICCProfile& iccProfile, const cmsHPROFILE& monitorProfile)
219         {
220             cmsHPROFILE inputICC = NULL;
221             if (!iccProfile.empty())
222             {
223                 inputICC = cmsOpenProfileFromMem(iccProfile.data(), iccProfile.size());
224             };
225             // check type of input profile
226             if (inputICC != NULL)
227             {
228                 if (cmsGetColorSpace(inputICC) != cmsSigRgbData)
229                 {
230                     cmsCloseProfile(inputICC);
231                     inputICC = NULL;
232                 };
233             };
234             // if there is no icc profile in file fall back to sRGB
235             if (inputICC == NULL)
236             {
237                 inputICC = cmsCreate_sRGBProfile();
238             };
239             // now build transform
240             cmsHTRANSFORM transform = cmsCreateTransform(inputICC, TYPE_RGB_8,
241                 monitorProfile, TYPE_RGB_8,
242                 INTENT_PERCEPTUAL, cmsFLAGS_BLACKPOINTCOMPENSATION);
243             // check that we could get a valid transform
244             if (transform != NULL)
245             {
246                 unsigned char* imgData = image.GetData();
247                 const int imgWidth = image.GetWidth();
248                 const int imgHeight = image.GetHeight();
249 #pragma omp parallel for
250                 for (int y = 0; y < imgHeight; ++y)
251                 {
252                     cmsDoTransform(transform, imgData + 3 * y * imgWidth, imgData + 3 * y * imgWidth, imgWidth);
253                 };
254                 cmsDeleteTransform(transform);
255             };
256             cmsCloseProfile(inputICC);
257         };
258 
259     }; // namespace Color
260 }; // namespace HuginBase
261