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