// vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2012 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ // Self #include "cmsprofile.h" // Local #include #include #include #include extern "C" { #include } // KDE #include // Qt #include // lcms #include // X11 #ifdef Q_WS_X11 #include #include #include #include #endif namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) kDebug() << x #else #define LOG(x) ; #endif namespace Cms { //- JPEG ----------------------------------------------------------------------- static cmsHPROFILE loadFromJpegData(const QByteArray& data) { cmsHPROFILE profile = 0; struct jpeg_decompress_struct srcinfo; JPEGErrorManager srcErrorManager; srcinfo.err = &srcErrorManager; jpeg_create_decompress(&srcinfo); if (setjmp(srcErrorManager.jmp_buffer)) { kError() << "libjpeg error in src\n"; return 0; } QBuffer buffer(const_cast(&data)); buffer.open(QIODevice::ReadOnly); IODeviceJpegSourceManager::setup(&srcinfo, &buffer); setup_read_icc_profile(&srcinfo); jpeg_read_header(&srcinfo, true); jpeg_start_decompress(&srcinfo); uchar* profile_data; uint profile_len; if (read_icc_profile(&srcinfo, &profile_data, &profile_len)) { LOG("Found a profile, length:" << profile_len); profile = cmsOpenProfileFromMem(profile_data, profile_len); } jpeg_destroy_decompress(&srcinfo); return profile; } //- Profile class -------------------------------------------------------------- struct ProfilePrivate { cmsHPROFILE mProfile; void reset() { if (mProfile) { cmsCloseProfile(mProfile); } mProfile = 0; } QString readInfo(cmsInfoType info) { GV_RETURN_VALUE_IF_FAIL(mProfile, QString()); wchar_t buffer[1024]; int size = cmsGetProfileInfo(mProfile, info, "en", "US", buffer, 1024); return QString::fromWCharArray(buffer, size); } }; Profile::Profile() : d(new ProfilePrivate) { d->mProfile = 0; } Profile::Profile(cmsHPROFILE hProfile) : d(new ProfilePrivate) { d->mProfile = hProfile; } Profile::~Profile() { d->reset(); delete d; } Profile::Ptr Profile::loadFromImageData(const QByteArray& data, const QByteArray& format) { Profile::Ptr ptr; cmsHPROFILE hProfile = 0; if (format == "png") { hProfile = loadFromPngData(data); } if (format == "jpeg") { hProfile = loadFromJpegData(data); } if (hProfile) { ptr = new Profile(hProfile); } return ptr; } Profile::Ptr Profile::loadFromExiv2Image(const Exiv2::Image* image) { Profile::Ptr ptr; cmsHPROFILE hProfile = 0; const Exiv2::ExifData& exifData = image->exifData(); Exiv2::ExifKey key("Exif.Image.InterColorProfile"); Exiv2::ExifData::const_iterator it = exifData.findKey(key); if (it == exifData.end()) { LOG("No profile found"); return ptr; } int size = it->size(); LOG("size:" << size); QByteArray data; data.resize(size); it->copy(reinterpret_cast(data.data()), Exiv2::invalidByteOrder); hProfile = cmsOpenProfileFromMem(data, size); if (hProfile) { ptr = new Profile(hProfile); } return ptr; } cmsHPROFILE Profile::handle() const { return d->mProfile; } QString Profile::copyright() const { return d->readInfo(cmsInfoCopyright); } QString Profile::description() const { return d->readInfo(cmsInfoDescription); } QString Profile::manufacturer() const { return d->readInfo(cmsInfoManufacturer); } QString Profile::model() const { return d->readInfo(cmsInfoModel); } Profile::Ptr Profile::getMonitorProfile() { cmsHPROFILE hProfile = 0; // Get the profile from you config file if the user has set it. // if the user allows override through the atom, do this: #ifdef Q_WS_X11 // get the current screen... int screen = -1; Atom type; int format; unsigned long nitems; unsigned long bytes_after; quint8 *str; static Atom icc_atom = XInternAtom(QX11Info::display(), "_ICC_PROFILE", True); if (XGetWindowProperty(QX11Info::display(), QX11Info::appRootWindow(screen), icc_atom, 0, INT_MAX, False, XA_CARDINAL, &type, &format, &nitems, &bytes_after, (unsigned char **) &str) == Success ) { hProfile = cmsOpenProfileFromMem((void*)str, nitems); } #endif if (!hProfile) { hProfile = cmsCreate_sRGBProfile(); } return Profile::Ptr(new Profile(hProfile)); } } // namespace Cms } // namespace Gwenview