File indexing completed on 2022-09-27 13:43:48

0001 /********************************************************************
0002  KolorServer - color server based on the X Color Management Specification
0003  This file is part of the KDE project.
0004 
0005 Copyright (C) 2012 Casian Andrei <skeletk13@gmail.com>
0006 
0007 Redistribution and use in source and binary forms, with or without
0008 modification, are permitted provided that the following conditions
0009 are met:
0010 
0011 1. Redistributions of source code must retain the above copyright
0012    notice, this list of conditions and the following disclaimer.
0013 2. Redistributions in binary form must reproduce the above copyright
0014    notice, this list of conditions and the following disclaimer in the
0015    documentation and/or other materials provided with the distribution.
0016 
0017 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0018 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0019 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0020 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0021 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0022 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0023 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0024 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0025 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0026 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0027 *********************************************************************/
0028 
0029 #include <KDebug>
0030 
0031 #include "color-context.h"
0032 
0033 #include "display.h"
0034 
0035 #include <oyranos_devices.h>
0036 #include <oyConversion_s.h>
0037 
0038 namespace KolorServer
0039 {
0040 
0041 /*
0042  * Color Context
0043  */
0044 
0045 ColorContext::ColorContext()
0046     : m_srcProfile(NULL)
0047     , m_dstProfile(NULL)
0048 {
0049     buildDummyClut(m_clut);
0050     /* select profiles matching actual capabilities */
0051     icc_profile_flags = oyICCProfileSelectionFlagsFromOptions( OY_CMM_STD, "//" OY_TYPE_STD "/icc_color", NULL, 0 );
0052 }
0053 
0054 ColorContext::~ColorContext()
0055 {
0056     // Release profiles
0057     if (m_srcProfile)
0058         oyProfile_Release(&m_srcProfile);
0059     if (m_dstProfile)
0060         oyProfile_Release(&m_dstProfile);
0061 }
0062 
0063 oyProfile_s *ColorContext::sourceProfile()
0064 {
0065     return m_srcProfile;
0066 }
0067 
0068 oyProfile_s *ColorContext::destinationProfile()
0069 {
0070     return m_dstProfile;
0071 }
0072 
0073 void ColorContext::setDestinationProfile(oyProfile_s *profile)
0074 {
0075     oyProfile_Release(&m_dstProfile);
0076     m_dstProfile = profile;
0077 }
0078 
0079 const QString& ColorContext::outputName()
0080 {
0081     return m_outputName;
0082 }
0083 
0084 const Clut& ColorContext::colorLookupTable() const
0085 {
0086     return m_clut;
0087 }
0088 
0089 void ColorContext::setupColorLookupTable(bool advanced)
0090 {
0091     kDebug() << m_outputName;
0092 
0093     oyProfile_s *dummyProfile = 0;
0094     oyOptions_s *options = 0;
0095 
0096     if (!m_dstProfile)
0097         m_dstProfile = dummyProfile = oyProfile_FromStd(oyASSUMED_WEB, icc_profile_flags, 0);
0098 
0099     /* skip dummyProfile to dummyProfile conversion */
0100     if (!m_srcProfile && dummyProfile) {
0101         if (dummyProfile)
0102             oyProfile_Release(&dummyProfile);
0103         return;
0104     }
0105 
0106     if (!m_srcProfile) {
0107         m_srcProfile = oyProfile_FromStd(oyASSUMED_WEB, icc_profile_flags, 0);
0108         if (!m_srcProfile) {
0109             kError() << "Output" << m_outputName << ":" << "no assumed dummyProfile source profile";
0110             kWarning() << "Output" << m_outputName << "using dummy clut";
0111             buildDummyClut(m_clut);
0112             return;
0113         }
0114     }
0115 
0116     int error = 0;
0117     int flags = 0;
0118 
0119     // Optionally set advanced options from Oyranos
0120     if (advanced)
0121         flags = oyOPTIONATTRIBUTE_ADVANCED;
0122 
0123     // Allocate memory for clut data
0124     m_clut.resize(CLUT_ELEMENT_COUNT);
0125 
0126     kDebug() << "Color conversion for" << m_outputName << "flags" << flags << (advanced ? "advanced" : "");
0127     oyImage_s *imageIn = oyImage_Create(
0128         LUT_GRID_POINTS,
0129         LUT_GRID_POINTS * LUT_GRID_POINTS,
0130         m_clut.data(),
0131         OY_TYPE_123_16,
0132         m_srcProfile,
0133         0);
0134     oyImage_s *imageOut = oyImage_Create(
0135         LUT_GRID_POINTS,
0136         LUT_GRID_POINTS * LUT_GRID_POINTS,
0137         m_clut.data(),
0138         OY_TYPE_123_16,
0139         m_dstProfile,
0140         0);
0141 
0142     oyConversion_s *conversion = oyConversion_CreateBasicPixels(imageIn, imageOut, options, 0);
0143     if (!conversion) {
0144         kWarning() << "No conversion created for" << m_outputName;
0145         if (dummyProfile)
0146             oyProfile_Release(&dummyProfile);
0147         return;
0148     }
0149     oyOptions_Release(&options);
0150 
0151     error = oyOptions_SetFromText(&options, "//"OY_TYPE_STD"/config/display_mode", "1", OY_CREATE_NEW);
0152     if (error) {
0153         kWarning() << "Oy options error:" << error;
0154         if (dummyProfile)
0155             oyProfile_Release(&dummyProfile);
0156         return;
0157     }
0158     error = oyConversion_Correct(conversion, "//"OY_TYPE_STD"/icc_color", flags, options);
0159     if (error > 0) {
0160         kWarning() << "Failed to correct conversion for" << m_outputName << "flags" << flags;
0161         if (dummyProfile)
0162             oyProfile_Release(&dummyProfile);
0163         return;
0164     }
0165 
0166     oyFilterGraph_s *conversionGraph = oyConversion_GetGraph(conversion);
0167     oyFilterNode_s *iccNode = oyFilterGraph_GetNode(conversionGraph, -1, "///icc", 0);
0168 
0169     // See what to search for in the cache
0170     QByteArray entryText;
0171     const char *t = oyFilterNode_GetText(iccNode, oyNAME_NAME);
0172     if (t)
0173         entryText = t;
0174 
0175     oyStructList_s *cache = Display::getInstance()->cache();
0176     oyHash_s *entry = oyStructList_GetHash(cache, 0, entryText.constData());
0177     oyArray2d_s *oyClut = (oyArray2d_s*) oyHash_GetPointer(entry, oyOBJECT_ARRAY2D_S);
0178     char ** array2d = (char**)oyArray2d_GetData( oyClut );
0179 
0180     oyFilterNode_Release(&iccNode);
0181     oyFilterGraph_Release(&conversionGraph);
0182 
0183     if (oyClut) {
0184         // Found in cache
0185         kDebug() << "clut" << oyClut << "obtained from cache using entry" << entryText;
0186         memcpy(m_clut.data(), array2d[0], CLUT_DATA_SIZE);
0187     } else {
0188         kDebug() << "clut not found in cache using entry" << entryText << ", doing conversion";
0189 
0190         // Create dummy / identity clut data for conversion input
0191         buildDummyClut(m_clut);
0192 
0193         // Do conversion
0194         error = oyConversion_RunPixels(conversion, 0);
0195         if (error) {
0196             kWarning() << "Output" << m_outputName << "Error" << error << "in conversion run pixels";
0197             if (dummyProfile)
0198                 oyProfile_Release(&dummyProfile);
0199             return;
0200         }
0201 
0202         // Save to cache
0203         oyClut = oyArray2d_Create(
0204             NULL,
0205             LUT_GRID_POINTS * 3,
0206             LUT_GRID_POINTS * LUT_GRID_POINTS,
0207             oyUINT16,
0208             NULL);
0209         array2d = (char**)oyArray2d_GetData( oyClut );
0210         memcpy(array2d[0], m_clut.data(), CLUT_DATA_SIZE);
0211         oyHash_SetPointer(entry, (oyStruct_s*) oyClut);
0212     }
0213 
0214     oyOptions_Release(&options);
0215     oyImage_Release(&imageIn);
0216     oyImage_Release(&imageOut);
0217     oyConversion_Release(&conversion);
0218 
0219     if (!m_dstProfile)
0220         kDebug() << "Output" << m_outputName << "no profile";
0221 }
0222 
0223 void ColorContext::setup(const QString &name)
0224 {
0225     kDebug();
0226     if (!Display::getInstance()->colorDesktopActivated())
0227         return;
0228 
0229     m_srcProfile = oyProfile_FromStd(oyASSUMED_WEB, icc_profile_flags, 0);
0230     m_outputName = name;
0231     if (!m_srcProfile)
0232         kWarning() << "Output" << name << "no sRGB source profile";
0233 
0234     setupColorLookupTable(Display::getInstance()->isAdvancedIccDisplay());
0235 }
0236 
0237 bool ColorContext::getDeviceProfile(oyConfig_s *device)
0238 {
0239     kDebug() << device;
0240 
0241     oyProfile_Release(&m_dstProfile);
0242 
0243     oyOptions_s *options = 0;
0244     oyOptions_SetFromText(&options, "//"OY_TYPE_STD"/config/command", "list", OY_CREATE_NEW);
0245     oyOptions_SetFromText(&options,
0246         "//"OY_TYPE_STD"/config/icc_profile.x_color_region_target",
0247         "yes", OY_CREATE_NEW );
0248     int error = oyDeviceGetProfile(device, options, &m_dstProfile);
0249     oyOptions_Release(&options);
0250 
0251     if (m_dstProfile) {
0252         /* check that no sRGB is delivered */
0253         if (error) {
0254             oyProfile_s *dummyProfile = oyProfile_FromStd(oyASSUMED_WEB, icc_profile_flags, 0);
0255             if (oyProfile_Equal(dummyProfile, m_dstProfile)) {
0256                 kWarning() << "Output" << m_outputName << "ignoring fallback, error" << error;
0257                 oyProfile_Release(&m_dstProfile);
0258                 error = 1;
0259             } else
0260                 error = 0;
0261             oyProfile_Release(&dummyProfile);
0262         }
0263     } else {
0264         kWarning() << "Output" << m_outputName << ": no ICC profile found, error" << error;
0265         error = 1;
0266     }
0267 
0268     return error == 0;
0269 }
0270 
0271 } // KolorServer namespace