File indexing completed on 2024-05-19 04:21:15
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2012 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "cmsprofile.h" 0023 #include <config-gwenview.h> 0024 0025 // Local 0026 #include <cms/cmsprofile_png.h> 0027 #include <gvdebug.h> 0028 #include <iodevicejpegsourcemanager.h> 0029 #include <jpegerrormanager.h> 0030 0031 extern "C" { 0032 #include <cms/iccjpeg.h> 0033 } 0034 0035 // KF 0036 0037 // Qt 0038 #include <QBuffer> 0039 0040 // lcms 0041 #include <lcms2.h> 0042 0043 // Exiv2 0044 #include <exiv2/exiv2.hpp> 0045 0046 // X11 0047 #ifdef HAVE_X11 0048 #include <X11/Xatom.h> 0049 #include <X11/Xlib.h> 0050 #include <fixx11h.h> 0051 #include <private/qtx11extras_p.h> 0052 #endif 0053 0054 // local 0055 #include "gwenview_lib_debug.h" 0056 #include <lib/gwenviewconfig.h> 0057 0058 namespace Gwenview 0059 { 0060 0061 #undef ENABLE_LOG 0062 #undef LOG 0063 //#define ENABLE_LOG 0064 #ifdef ENABLE_LOG 0065 #define LOG(x) //qCDebug(GWENVIEW_LIB_LOG) << x 0066 #else 0067 #define LOG(x) ; 0068 #endif 0069 0070 namespace Cms 0071 { 0072 0073 //- JPEG ----------------------------------------------------------------------- 0074 static cmsHPROFILE loadFromJpegData(const QByteArray& data) 0075 { 0076 cmsHPROFILE profile = nullptr; 0077 struct jpeg_decompress_struct srcinfo; 0078 0079 JPEGErrorManager srcErrorManager; 0080 srcinfo.err = &srcErrorManager; 0081 jpeg_create_decompress(&srcinfo); 0082 if (setjmp(srcErrorManager.jmp_buffer)) { 0083 qCCritical(GWENVIEW_LIB_LOG) << "libjpeg error in src\n"; 0084 return nullptr; 0085 } 0086 0087 QBuffer buffer(const_cast<QByteArray*>(&data)); 0088 buffer.open(QIODevice::ReadOnly); 0089 IODeviceJpegSourceManager::setup(&srcinfo, &buffer); 0090 0091 setup_read_icc_profile(&srcinfo); 0092 jpeg_read_header(&srcinfo, true); 0093 jpeg_start_decompress(&srcinfo); 0094 0095 uchar* profile_data; 0096 uint profile_len; 0097 if (read_icc_profile(&srcinfo, &profile_data, &profile_len)) { 0098 LOG("Found a profile, length:" << profile_len); 0099 profile = cmsOpenProfileFromMem(profile_data, profile_len); 0100 } 0101 0102 jpeg_destroy_decompress(&srcinfo); 0103 free(profile_data); 0104 0105 return profile; 0106 } 0107 0108 //- Profile class -------------------------------------------------------------- 0109 struct ProfilePrivate 0110 { 0111 cmsHPROFILE mProfile; 0112 0113 void reset() 0114 { 0115 if (mProfile) { 0116 cmsCloseProfile(mProfile); 0117 } 0118 mProfile = nullptr; 0119 } 0120 0121 QString readInfo(cmsInfoType info) 0122 { 0123 GV_RETURN_VALUE_IF_FAIL(mProfile, QString()); 0124 wchar_t buffer[1024]; 0125 int size = cmsGetProfileInfo(mProfile, info, "en", "US", buffer, 1024); 0126 return QString::fromWCharArray(buffer, size); 0127 } 0128 }; 0129 0130 Profile::Profile() 0131 : d(new ProfilePrivate) 0132 { 0133 d->mProfile = nullptr; 0134 } 0135 0136 Profile::Profile(cmsHPROFILE hProfile) 0137 : d(new ProfilePrivate) 0138 { 0139 d->mProfile = hProfile; 0140 } 0141 0142 Profile::~Profile() 0143 { 0144 d->reset(); 0145 delete d; 0146 } 0147 0148 Profile::Ptr Profile::loadFromImageData(const QByteArray& data, const QByteArray& format) 0149 { 0150 Profile::Ptr ptr; 0151 cmsHPROFILE hProfile = nullptr; 0152 if (format == "png") { 0153 hProfile = loadFromPngData(data); 0154 } else if (format == "jpeg") { 0155 hProfile = loadFromJpegData(data); 0156 } 0157 if (hProfile) { 0158 ptr = new Profile(hProfile); 0159 } 0160 return ptr; 0161 } 0162 0163 Profile::Ptr Profile::loadFromExiv2Image(const Exiv2::Image* image) 0164 { 0165 Profile::Ptr ptr; 0166 cmsHPROFILE hProfile = nullptr; 0167 0168 const Exiv2::ExifData& exifData = image->exifData(); 0169 Exiv2::ExifKey key("Exif.Image.InterColorProfile"); 0170 auto it = exifData.findKey(key); 0171 if (it == exifData.end()) { 0172 LOG("No profile found"); 0173 return ptr; 0174 } 0175 int size = it->size(); 0176 LOG("size:" << size); 0177 0178 QByteArray data; 0179 data.resize(size); 0180 it->copy(reinterpret_cast<Exiv2::byte*>(data.data()), Exiv2::invalidByteOrder); 0181 hProfile = cmsOpenProfileFromMem(data.constData(), size); 0182 0183 if (hProfile) { 0184 ptr = new Profile(hProfile); 0185 } 0186 return ptr; 0187 } 0188 0189 Profile::Ptr Profile::loadFromICC(const QByteArray &data) 0190 { 0191 Profile::Ptr ptr; 0192 int size = data.size(); 0193 0194 if (size > 0) { 0195 cmsHPROFILE hProfile = cmsOpenProfileFromMem(data.constData(), size); 0196 0197 if (hProfile) { 0198 ptr = new Profile(hProfile); 0199 } 0200 } 0201 0202 return ptr; 0203 } 0204 0205 cmsHPROFILE Profile::handle() const 0206 { 0207 return d->mProfile; 0208 } 0209 0210 QString Profile::copyright() const 0211 { 0212 return d->readInfo(cmsInfoCopyright); 0213 } 0214 0215 QString Profile::description() const 0216 { 0217 return d->readInfo(cmsInfoDescription); 0218 } 0219 0220 QString Profile::manufacturer() const 0221 { 0222 return d->readInfo(cmsInfoManufacturer); 0223 } 0224 0225 QString Profile::model() const 0226 { 0227 return d->readInfo(cmsInfoModel); 0228 } 0229 0230 Profile::Ptr Profile::getMonitorProfile() 0231 { 0232 cmsHPROFILE hProfile = nullptr; 0233 // Get the profile from you config file if the user has set it. 0234 // if the user allows override through the atom, do this: 0235 #ifdef HAVE_X11 0236 if (QX11Info::isPlatformX11() && GwenviewConfig::enableColorManagement()) { 0237 // get the current screen... 0238 int screen = -1; 0239 0240 Atom type; 0241 int format; 0242 unsigned long nitems; 0243 unsigned long bytes_after; 0244 quint8 *str; 0245 0246 static Atom icc_atom = XInternAtom(QX11Info::display(), "_ICC_PROFILE", True); 0247 0248 if (XGetWindowProperty(QX11Info::display(), 0249 QX11Info::appRootWindow(screen), 0250 icc_atom, 0251 0, 0252 INT_MAX, 0253 False, 0254 XA_CARDINAL, 0255 &type, 0256 &format, 0257 &nitems, 0258 &bytes_after, 0259 (unsigned char **) &str) == Success 0260 ) { 0261 hProfile = cmsOpenProfileFromMem((void*)str, nitems); 0262 } 0263 } 0264 #endif 0265 if (!hProfile) { 0266 return getSRgbProfile(); 0267 } 0268 return Profile::Ptr(new Profile(hProfile)); 0269 } 0270 0271 Profile::Ptr Profile::getSRgbProfile() 0272 { 0273 return Profile::Ptr(new Profile(cmsCreate_sRGBProfile())); 0274 } 0275 0276 } // namespace Cms 0277 0278 } // namespace Gwenview