2
* Copyright (c) 2008,2010 Cyrille Berger <cberger@cberger.net>
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Lesser General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Lesser General Public License for more details.
14
* You should have received a copy of the GNU Lesser General Public License
15
* along with this library; see the file COPYING.LIB. If not, write to
16
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
* Boston, MA 02110-1301, USA.
20
#include "KoCtlColorProfile.h"
24
#include "DebugPigment.h"
26
#include <QDomDocument>
28
#include <QMutexLocker>
32
#include <KoChannelInfo.h>
33
#include <KoColorSpace.h>
34
#include <KoColorModelStandardIds.h>
35
#include <KoColorSpaceRegistry.h>
36
#include <KoCtlColorConversionTransformation.h>
37
#include <KoUniqueNumberForIdServer.h>
39
#include <GTLCore/Version.h>
41
#if GTL_CORE_VERSION_MAJOR == 0 && GTL_CORE_VERSION_MINOR == 9 && GTL_CORE_VERSION_REVISION > 12
42
#include <GTLCore/CompilationMessages.h>
45
#include <GTLCore/PixelDescription.h>
46
#include <GTLCore/String.h>
47
#include <GTLCore/Type.h>
48
#include <GTLCore/Value.h>
49
#include <OpenCTL/Program.h>
50
#include <OpenCTL/Module.h>
51
#include "KoCtlMutex.h"
52
#include "KoCtlParser.h"
53
#include "KoCtlUtils.h"
55
#include "kis_debug.h"
56
#include <kis_gtl_lock.h>
58
struct ConversionInfo {
59
QString sourceColorModelID;
60
QString sourceColorDepthID;
61
QString sourceProfile;
62
QString destinationColorModelID;
63
QString destinationColorDepthID;
64
QString destinationProfile;
68
struct KoCtlColorProfile::Private {
69
OpenCTL::Module* module;
70
QList<ConversionInfo> conversionInfos;
72
quint32 colorModelIDNumber;
74
quint32 colorDepthIDNumber;
76
qreal middleGrayScaleFactor;
77
QString profileSource;
78
bool loadFromSource(KoCtlColorProfile* self);
81
KoCtlColorProfile::KoCtlColorProfile() : KoColorProfile(), d(new Private)
84
d->middleGrayScaleFactor = 0.0883883;
85
d->exposure = pow(2, 2.47393) * d->middleGrayScaleFactor;
88
KoCtlColorProfile* KoCtlColorProfile::fromFile(QString fileName)
90
KoCtlColorProfile* profile = new KoCtlColorProfile;
91
profile->setFileName(fileName);
95
KoCtlColorProfile* KoCtlColorProfile::fromString(QString string)
97
KoCtlColorProfile* profile = new KoCtlColorProfile;
98
profile->d->profileSource = string;
99
if (profile->d->loadFromSource(profile)) {
107
KoCtlColorProfile::KoCtlColorProfile(const KoCtlColorProfile& rhs) : KoColorProfile(rhs), d(new Private(*rhs.d))
112
KoCtlColorProfile::~KoCtlColorProfile()
118
KoColorProfile* KoCtlColorProfile::clone() const
120
return new KoCtlColorProfile(*this);
123
bool KoCtlColorProfile::valid() const
125
dbgPigment << d->colorModelID.isNull() << " " << d->colorDepthID.isNull() << " isCompiled: " << d->module->isCompiled();
126
return (d->module && d->module->isCompiled() && !d->colorModelID.isNull() && !d->colorDepthID.isNull());
129
bool KoCtlColorProfile::isSuitableForOutput() const
134
bool KoCtlColorProfile::isSuitableForPrinting() const
139
bool KoCtlColorProfile::isSuitableForDisplay() const
144
OpenCTL::Program* KoCtlColorProfile::createColorConversionProgram(const KoColorSpace* _srcCs, const KoColorSpace* _dstCs) const
146
KisGtlLocker gtlLocker;
147
QString srcModelId = _srcCs->colorModelId().id();
148
QString srcDepthId = _srcCs->colorDepthId().id();
149
QString dstModelId = _dstCs->colorModelId().id();
150
QString dstDepthId = _dstCs->colorDepthId().id();
151
foreach(ConversionInfo info, d->conversionInfos) {
152
if (info.sourceColorModelID == srcModelId
153
&& (info.sourceColorDepthID == srcDepthId || (info.sourceColorDepthID == "F"
154
&& (srcDepthId == Float16BitsColorDepthID.id() || srcDepthId == Float32BitsColorDepthID.id())))
155
&& info.destinationColorModelID == dstModelId
156
&& (info.destinationColorDepthID == dstDepthId || (info.destinationColorDepthID == "F"
157
&& (dstDepthId == Float16BitsColorDepthID.id() || dstDepthId == Float32BitsColorDepthID.id())))) {
158
GTLCore::PixelDescription srcPixelDescription = createPixelDescription(_srcCs);
159
GTLCore::PixelDescription dstPixelDescription = createPixelDescription(_dstCs);
160
return new OpenCTL::Program(info.function.toAscii().data(), d->module, srcPixelDescription, dstPixelDescription);
166
QList<KoColorConversionTransformationFactory*> KoCtlColorProfile::createColorConversionTransformationFactories() const
168
dbgPlugins << "createColorConversionTransformationFactories() " << d->conversionInfos.size();
169
QList<KoColorConversionTransformationFactory*> factories;
170
foreach(ConversionInfo info, d->conversionInfos) {
171
dbgPlugins << info.destinationColorModelID << " " << info.destinationColorDepthID << " " << info.destinationProfile << " " << info.sourceColorModelID << " " << info.sourceColorDepthID << " " << info.sourceProfile << " " << info.function;
172
if (info.sourceColorDepthID == "F" && info.destinationColorDepthID == "F") {
174
new KoCtlColorConversionTransformationFactory(
175
info.sourceColorModelID, Float16BitsColorDepthID.id(), info.sourceProfile,
176
info.destinationColorModelID, Float16BitsColorDepthID.id(), info.destinationProfile));
178
new KoCtlColorConversionTransformationFactory(
179
info.sourceColorModelID, Float32BitsColorDepthID.id(), info.sourceProfile,
180
info.destinationColorModelID, Float32BitsColorDepthID.id(), info.destinationProfile));
181
} else if (info.sourceColorDepthID == "F") {
183
new KoCtlColorConversionTransformationFactory(
184
info.sourceColorModelID, Float16BitsColorDepthID.id(),
185
info.sourceProfile, info.destinationColorModelID, info.destinationColorDepthID,
186
info.destinationProfile));
188
new KoCtlColorConversionTransformationFactory(
189
info.sourceColorModelID, Float32BitsColorDepthID.id(),
190
info.sourceProfile, info.destinationColorModelID, info.destinationColorDepthID,
191
info.destinationProfile));
192
} else if (info.destinationColorDepthID == "F") {
194
new KoCtlColorConversionTransformationFactory(
195
info.sourceColorModelID, info.sourceColorDepthID,
196
info.sourceProfile, info.destinationColorModelID, Float16BitsColorDepthID.id(),
197
info.destinationProfile));
199
new KoCtlColorConversionTransformationFactory(
200
info.sourceColorModelID, info.sourceColorDepthID,
201
info.sourceProfile, info.destinationColorModelID, Float32BitsColorDepthID.id(),
202
info.destinationProfile));
205
new KoCtlColorConversionTransformationFactory(
206
info.sourceColorModelID, info.sourceColorDepthID,
207
info.sourceProfile, info.destinationColorModelID, info.destinationColorDepthID,
208
info.destinationProfile));
214
bool KoCtlColorProfile::operator==(const KoColorProfile& p) const
216
const KoCtlColorProfile* ctlp = dynamic_cast<const KoCtlColorProfile*>(&p);
218
return ctlp->name() == name() && ctlp->d->colorModelIDNumber == d->colorModelIDNumber && ctlp->d->colorDepthIDNumber == d->colorDepthIDNumber;
224
QString KoCtlColorProfile::colorModel() const
226
return d->colorModelID;
229
QString KoCtlColorProfile::colorDepth() const
231
return d->colorDepthID;
234
void KoCtlColorProfile::decodeTransformations(QDomElement& elt)
236
dbgPlugins << "decodeTransformations " << elt.tagName();
237
for (QDomNode nt = elt.firstChild(); !nt.isNull(); nt = nt.nextSibling()) {
238
QDomElement et = nt.toElement();
240
dbgPigment << et.tagName();
241
if (et.tagName() == "conversions") {
242
decodeConversions(et);
248
void KoCtlColorProfile::decodeConversions(QDomElement& elt)
250
dbgPlugins << "decodeConversions " << elt.tagName() << " " << elt.childNodes().count();
251
for (QDomNode n = elt.firstChild(); !n.isNull(); n = n.nextSibling()) {
252
QDomElement e = n.toElement();
254
dbgPigment << e.tagName();
255
if (e.tagName() == "conversion") {
256
QDomElement eIn = e.firstChildElement("input");
257
QDomElement eOut = e.firstChildElement("output");
258
if (!eIn.isNull() && !eOut.isNull()) {
260
ci.function = e.attribute("function");
261
ci.sourceColorModelID = eIn.attribute("colorModel");
262
ci.sourceColorDepthID = KoCtlParser::generateDepthID(eIn.attribute("depth"), eIn.attribute("type")).id();
263
ci.sourceProfile = eIn.attribute("profile");
264
ci.destinationColorModelID = eOut.attribute("colorModel");
265
ci.destinationColorDepthID = KoCtlParser::generateDepthID(eOut.attribute("depth"), eOut.attribute("type")).id();
266
ci.destinationProfile = eOut.attribute("profile");
267
if (ci.sourceColorModelID == colorModel() && ci.sourceColorDepthID == colorDepth() && ci.sourceProfile.isEmpty()) {
268
ci.sourceProfile = name();
269
d->conversionInfos.push_back(ci);
270
} else if (ci.destinationColorModelID == colorModel() && ci.destinationColorDepthID == colorDepth() && ci.destinationProfile.isEmpty()) {
271
ci.destinationProfile = name();
272
d->conversionInfos.push_back(ci);
274
Q_ASSERT(ci.destinationProfile == name() || ci.sourceProfile == name());
275
d->conversionInfos.push_back(ci);
278
dbgPigment << "Invalid conversion, missing <input> or <output> or both";
283
dbgPigment << d->conversionInfos.size() << " convertions were found";
286
bool KoCtlColorProfile::load()
288
QFile file(fileName());
289
if (!file.open(QIODevice::ReadOnly)) {
290
dbgPigment << "Can't open file : " << fileName();
293
d->profileSource = file.readAll();
295
return d->loadFromSource(this);
298
bool KoCtlColorProfile::Private::loadFromSource(KoCtlColorProfile* self)
303
if (!doc.setContent(profileSource, &errorMsg, &errorLine)) {
304
dbgPigment << "Can't parse profile : " << self->fileName() << " Error at line " << errorLine << " " << errorMsg;
307
QDomElement docElem = doc.documentElement();
308
if (docElem.tagName() != "ctlprofile") {
309
dbgPigment << "Not a ctlprofile, root tag was : " << docElem.tagName();
312
QDomNode n = docElem.firstChild();
313
while (!n.isNull()) {
314
QDomElement e = n.toElement();
316
dbgPigment << e.tagName();
317
if (e.tagName() == "info") {
318
self->setName(e.attribute("name"));
319
colorDepthID = KoCtlParser::generateDepthID(e.attribute("depth"), e.attribute("type")).id();
320
colorDepthIDNumber = KoUniqueNumberForIdServer::instance()->numberForId(colorDepthID);
321
colorModelID = e.attribute("colorModel");
322
colorModelIDNumber = KoUniqueNumberForIdServer::instance()->numberForId(colorModelID);
323
dbgPigment << "colorModel = " << e.attribute("colorModel");
324
} else if (e.tagName() == "program") {
325
QDomNode nCDATA = e.firstChild();
326
if (!nCDATA.isNull()) {
327
QMutexLocker lock(ctlMutex);
328
QDomCDATASection CDATA = nCDATA.toCDATASection();
329
dbgPigment << CDATA.data();
330
module = new OpenCTL::Module();
331
module->setSource(self->name().toAscii().data(), CDATA.data().toAscii().data());
333
#if GTL_CORE_VERSION_MAJOR == 0 && GTL_CORE_VERSION_MINOR == 9 && GTL_CORE_VERSION_REVISION > 12
334
if (!module->isCompiled()) {
335
dbgKrita << module->compilationMessages().toString().c_str();
339
} else if (e.tagName() == "transformations") {
340
self->decodeTransformations(e);
348
bool KoCtlColorProfile::save(const QString &fileName)
350
QFile file(fileName);
351
if (!file.open(QIODevice::WriteOnly)) {
352
dbgPigment << "Can't open file : " << fileName;
355
file.write(d->profileSource.toUtf8());
360
QByteArray KoCtlColorProfile::rawData() const
362
return d->profileSource.toUtf8();
365
QVariant KoCtlColorProfile::property(const QString& _name) const
367
if (_name == "exposure") {
370
dbgPigment << "Not CTL property " << _name;
371
return KoColorProfile::property(_name);
375
void KoCtlColorProfile::setProperty(const QString& _name, const QVariant& _variant)
377
if (_name == "exposure") {
378
d->exposure = pow(2, _variant.toDouble() + 2.47393) * d->middleGrayScaleFactor;
380
dbgPigment << "Not CTL property " << _name;
381
return KoColorProfile::setProperty(_name, _variant);