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