211 if (path.isEmpty()) {
214 cmsHPROFILE handle = cmsOpenProfileFromFile(path.toUtf8(),
"r");
216 qCWarning(KWIN_CORE) <<
"Failed to open color profile file:" << path;
219 if (cmsGetDeviceClass(handle) != cmsSigDisplayClass) {
220 qCWarning(KWIN_CORE) <<
"Only Display ICC profiles are supported";
223 if (cmsGetPCS(handle) != cmsColorSpaceSignature::cmsSigXYZData) {
224 qCWarning(KWIN_CORE) <<
"Only ICC profiles with a XYZ connection space are supported";
227 if (cmsGetColorSpace(handle) != cmsColorSpaceSignature::cmsSigRgbData) {
228 qCWarning(KWIN_CORE) <<
"Only ICC profiles with RGB color spaces are supported";
232 std::shared_ptr<ColorTransformation>
vcgt;
233 cmsToneCurve **vcgtTag =
static_cast<cmsToneCurve **
>(cmsReadTag(handle, cmsSigVcgtTag));
234 if (!vcgtTag || !vcgtTag[0]) {
235 qCDebug(KWIN_CORE) <<
"Profile" << path <<
"has no VCGT tag";
238 cmsToneCurve *toneCurves[] = {
239 cmsDupToneCurve(vcgtTag[0]),
240 cmsDupToneCurve(vcgtTag[1]),
241 cmsDupToneCurve(vcgtTag[2]),
243 std::vector<std::unique_ptr<ColorPipelineStage>> stages;
244 stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(
nullptr, 3, toneCurves)));
245 vcgt = std::make_shared<ColorTransformation>(std::move(stages));
248 const cmsCIEXYZ *whitepoint =
static_cast<cmsCIEXYZ *
>(cmsReadTag(handle, cmsSigMediaWhitePointTag));
250 qCWarning(KWIN_CORE,
"profile is missing the wtpt tag");
257 QVector3D white(whitepoint->X, whitepoint->Y, whitepoint->Z);
258 std::optional<QMatrix4x4> chromaticAdaptationMatrix;
259 if (cmsIsTag(handle, cmsSigChromaticAdaptationTag)) {
261 const auto data = readTagRaw(handle, cmsSigChromaticAdaptationTag);
262 const auto mat = parseMatrix(std::span(data).subspan(8),
false);
264 qCWarning(KWIN_CORE,
"Parsing chromatic adaptation matrix failed");
267 bool invertable =
false;
268 chromaticAdaptationMatrix = mat->inverted(&invertable);
270 qCWarning(KWIN_CORE,
"Inverting chromatic adaptation matrix failed");
273 const QVector3D D50(0.9642, 1.0, 0.8249);
274 white = *chromaticAdaptationMatrix * D50;
276 if (cmsCIExyYTRIPLE *chrmTag =
static_cast<cmsCIExyYTRIPLE *
>(cmsReadTag(handle, cmsSigChromaticityTag))) {
281 const cmsCIEXYZ *r =
static_cast<cmsCIEXYZ *
>(cmsReadTag(handle, cmsSigRedColorantTag));
282 const cmsCIEXYZ *g =
static_cast<cmsCIEXYZ *
>(cmsReadTag(handle, cmsSigGreenColorantTag));
283 const cmsCIEXYZ *b =
static_cast<cmsCIEXYZ *
>(cmsReadTag(handle, cmsSigBlueColorantTag));
284 if (!r || !g || !b) {
285 qCWarning(KWIN_CORE,
"rXYZ, gXYZ or bXYZ tag is missing");
288 if (chromaticAdaptationMatrix) {
289 red = *chromaticAdaptationMatrix * QVector3D(r->X, r->Y, r->Z);
290 green = *chromaticAdaptationMatrix * QVector3D(g->X, g->Y, g->Z);
291 blue = *chromaticAdaptationMatrix * QVector3D(b->X, b->Y, b->Z);
294 cmsCIEXYZ adaptedR{};
295 cmsCIEXYZ adaptedG{};
296 cmsCIEXYZ adaptedB{};
297 bool success = cmsAdaptToIlluminant(&adaptedR, cmsD50_XYZ(), whitepoint, r);
298 success &= cmsAdaptToIlluminant(&adaptedG, cmsD50_XYZ(), whitepoint, g);
299 success &= cmsAdaptToIlluminant(&adaptedB, cmsD50_XYZ(), whitepoint, b);
303 red = QVector3D(adaptedR.X, adaptedR.Y, adaptedR.Z);
304 green = QVector3D(adaptedG.X, adaptedG.Y, adaptedG.Z);
305 blue = QVector3D(adaptedB.X, adaptedB.Y, adaptedB.Z);
309 if (red.y() == 0 || green.y() == 0 || blue.y() == 0 || white.y() == 0) {
310 qCWarning(KWIN_CORE,
"Profile has invalid primaries");
315 if (cmsIsTag(handle, cmsSigBToD1Tag) && !cmsIsTag(handle, cmsSigBToA1Tag) && !cmsIsTag(handle, cmsSigBToA0Tag)) {
316 qCWarning(KWIN_CORE,
"Profiles with only BToD tags aren't supported yet");
319 if (cmsIsTag(handle, cmsSigBToA1Tag)) {
321 auto data = parseBToATag(handle, cmsSigBToA1Tag);
323 return std::make_unique<IccProfile>(handle,
Colorimetry(red, green, blue, white), std::move(*data),
vcgt);
325 qCWarning(KWIN_CORE,
"Parsing BToA1 tag failed");
329 if (cmsIsTag(handle, cmsSigBToA0Tag)) {
331 auto data = parseBToATag(handle, cmsSigBToA0Tag);
333 return std::make_unique<IccProfile>(handle,
Colorimetry(red, green, blue, white), std::move(*data),
vcgt);
335 qCWarning(KWIN_CORE,
"Parsing BToA0 tag failed");
341 cmsToneCurve *r =
static_cast<cmsToneCurve *
>(cmsReadTag(handle, cmsSigRedTRCTag));
342 cmsToneCurve *g =
static_cast<cmsToneCurve *
>(cmsReadTag(handle, cmsSigGreenTRCTag));
343 cmsToneCurve *b =
static_cast<cmsToneCurve *
>(cmsReadTag(handle, cmsSigBlueTRCTag));
344 if (!r || !g || !b) {
345 qCWarning(KWIN_CORE) <<
"ICC profile is missing at least one TRC tag";
348 cmsToneCurve *toneCurves[] = {
349 cmsReverseToneCurveEx(4096, r),
350 cmsReverseToneCurveEx(4096, g),
351 cmsReverseToneCurveEx(4096, b),
353 std::vector<std::unique_ptr<ColorPipelineStage>> stages;
354 stages.push_back(std::make_unique<ColorPipelineStage>(cmsStageAllocToneCurves(
nullptr, 3, toneCurves)));
355 const auto inverseEOTF = std::make_shared<ColorTransformation>(std::move(stages));