1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2005-24-01
7  * Description : Chanels mixer filter
8  *
9  * Copyright (C) 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  * Copyright (C) 2010      by Martin Klapetek <martin dot klapetek at gmail dot com>
11  *
12  * This program is free software; you can redistribute it
13  * and/or modify it under the terms of the GNU General
14  * Public License as published by the Free Software Foundation;
15  * either version 2, or (at your option)
16  * any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * ============================================================ */
24 
25 #include "mixerfilter.h"
26 
27 // C++ includes
28 
29 #include <cstdio>
30 #include <cmath>
31 
32 // KDE includes
33 
34 #include <klocalizedstring.h>
35 
36 // Local includes
37 
38 #include "dimg.h"
39 #include "dcolor.h"
40 
41 namespace Digikam
42 {
43 
MixerFilter(QObject * const parent)44 MixerFilter::MixerFilter(QObject* const parent)
45     : DImgThreadedFilter(parent)
46 {
47     initFilter();
48 }
49 
MixerFilter(DImg * const orgImage,QObject * const parent,const MixerContainer & settings)50 MixerFilter::MixerFilter(DImg* const orgImage, QObject* const parent, const MixerContainer& settings)
51     : DImgThreadedFilter(orgImage, parent, QLatin1String("MixerFilter")),
52       m_settings        (settings)
53 {
54     initFilter();
55 }
56 
~MixerFilter()57 MixerFilter::~MixerFilter()
58 {
59     cancelFilter();
60 }
61 
DisplayableName()62 QString MixerFilter::DisplayableName()
63 {
64     return QString::fromUtf8(I18N_NOOP("Channel Mixer Tool"));
65 }
66 
filterImage()67 void MixerFilter::filterImage()
68 {
69     m_destImage.putImageData(m_orgImage.bits());
70 
71     uchar* bits     = m_destImage.bits();
72     uint width      = m_destImage.width();
73     uint height     = m_destImage.height();
74     bool sixteenBit = m_destImage.sixteenBit();
75 
76     uint size       = width * height;
77     int  progress;
78 
79     uint i;
80     double   rnorm  = 1;    // red channel normalizer use in RGB mode.
81     double   mnorm  = 1;    // monochrome normalizer used in Monochrome mode.
82 
83     if (m_settings.bMonochrome)
84     {
85         mnorm = CalculateNorm(m_settings.blackRedGain, m_settings.blackGreenGain,
86                               m_settings.blackBlueGain, m_settings.bPreserveLum);
87     }
88     else
89     {
90         rnorm = CalculateNorm(m_settings.redRedGain, m_settings.redGreenGain,
91                               m_settings.redBlueGain, m_settings.bPreserveLum);
92     }
93 
94     double gnorm = CalculateNorm(m_settings.greenRedGain, m_settings.greenGreenGain,
95                                  m_settings.greenBlueGain, m_settings.bPreserveLum);
96     double bnorm = CalculateNorm(m_settings.blueRedGain, m_settings.blueGreenGain,
97                                  m_settings.blueBlueGain, m_settings.bPreserveLum);
98 
99     if (!sixteenBit)        // 8 bits image.
100     {
101         uchar  nGray, red, green, blue;
102         uchar* ptr = bits;
103 
104         for (i = 0 ; i < size ; ++i)
105         {
106             blue  = ptr[0];
107             green = ptr[1];
108             red   = ptr[2];
109 
110             if (m_settings.bMonochrome)
111             {
112                 nGray  = MixPixel(m_settings.blackRedGain, m_settings.blackGreenGain, m_settings.blackBlueGain,
113                                   (unsigned short)red, (unsigned short)green, (unsigned short)blue,
114                                   sixteenBit, mnorm);
115                 ptr[0] = ptr[1] = ptr[2] = nGray;
116             }
117             else
118             {
119                 ptr[0] = (uchar)MixPixel(m_settings.blueRedGain, m_settings.blueGreenGain, m_settings.blueBlueGain,
120                                          (unsigned short)red, (unsigned short)green, (unsigned short)blue,
121                                          sixteenBit, bnorm);
122                 ptr[1] = (uchar)MixPixel(m_settings.greenRedGain, m_settings.greenGreenGain, m_settings.greenBlueGain,
123                                          (unsigned short)red, (unsigned short)green, (unsigned short)blue,
124                                          sixteenBit, gnorm);
125                 ptr[2] = (uchar)MixPixel(m_settings.redRedGain, m_settings.redGreenGain, m_settings.redBlueGain,
126                                          (unsigned short)red, (unsigned short)green, (unsigned short)blue,
127                                          sixteenBit, rnorm);
128             }
129 
130             ptr     += 4;
131 
132             progress = (int)(((double)i * 100.0) / size);
133 
134             if ((progress % 5) == 0)
135             {
136                 postProgress(progress);
137             }
138         }
139     }
140     else               // 16 bits image.
141     {
142         unsigned short  nGray, red, green, blue;
143         unsigned short* ptr = reinterpret_cast<unsigned short*>(bits);
144 
145         for (i = 0 ; i < size ; ++i)
146         {
147             blue  = ptr[0];
148             green = ptr[1];
149             red   = ptr[2];
150 
151             if (m_settings.bMonochrome)
152             {
153                 nGray  = MixPixel(m_settings.blackRedGain, m_settings.blackGreenGain, m_settings.blackBlueGain,
154                                   red, green, blue, sixteenBit, mnorm);
155                 ptr[0] = ptr[1] = ptr[2] = nGray;
156             }
157             else
158             {
159                 ptr[0] = MixPixel(m_settings.blueRedGain, m_settings.blueGreenGain, m_settings.blueBlueGain,
160                                   red, green, blue, sixteenBit, bnorm);
161                 ptr[1] = MixPixel(m_settings.greenRedGain, m_settings.greenGreenGain, m_settings.greenBlueGain,
162                                   red, green, blue, sixteenBit, gnorm);
163                 ptr[2] = MixPixel(m_settings.redRedGain, m_settings.redGreenGain, m_settings.redBlueGain,
164                                   red, green, blue, sixteenBit, rnorm);
165             }
166 
167             ptr     += 4;
168 
169             progress = (int)(((double)i * 100.0) / size);
170 
171             if ((progress % 5) == 0)
172             {
173                 postProgress(progress);
174             }
175         }
176     }
177 }
178 
CalculateNorm(double RedGain,double GreenGain,double BlueGain,bool bPreserveLum)179 double MixerFilter::CalculateNorm(double RedGain, double GreenGain, double BlueGain, bool bPreserveLum)
180 {
181     double lfSum = RedGain + GreenGain + BlueGain;
182 
183     if ((lfSum == 0.0) || (!bPreserveLum))
184     {
185         return (1.0);
186     }
187 
188     return (fabs(1.0 / lfSum));
189 }
190 
MixPixel(double RedGain,double GreenGain,double BlueGain,unsigned short R,unsigned short G,unsigned short B,bool sixteenBit,double Norm)191 unsigned short MixerFilter::MixPixel(double RedGain, double GreenGain, double BlueGain,
192                                      unsigned short R, unsigned short G, unsigned short B, bool sixteenBit,
193                                      double Norm)
194 {
195     double lfMix = Norm * (RedGain * (double)R + GreenGain * (double)G + BlueGain * (double)B);
196 
197     return ((unsigned short)CLAMP((int)lfMix, 0, sixteenBit ? 65535 : 255));
198 }
199 
filterAction()200 FilterAction MixerFilter::filterAction()
201 {
202     FilterAction action(FilterIdentifier(), CurrentVersion());
203     action.setDisplayableName(DisplayableName());
204 
205     action.addParameter(QLatin1String("blackBlueGain"),  m_settings.blackBlueGain);
206     action.addParameter(QLatin1String("blackGreenGain"), m_settings.blackGreenGain);
207     action.addParameter(QLatin1String("blackRedGain"),   m_settings.blackRedGain);
208     action.addParameter(QLatin1String("blueBlueGain"),   m_settings.blueBlueGain);
209     action.addParameter(QLatin1String("blueGreenGain"),  m_settings.blueGreenGain);
210     action.addParameter(QLatin1String("blueRedGain"),    m_settings.blueRedGain);
211     action.addParameter(QLatin1String("bMonochrome"),    m_settings.bMonochrome);
212     action.addParameter(QLatin1String("bPreserveLum"),   m_settings.bPreserveLum);
213     action.addParameter(QLatin1String("greenBlueGain"),  m_settings.greenBlueGain);
214     action.addParameter(QLatin1String("greenGreenGain"), m_settings.greenGreenGain);
215     action.addParameter(QLatin1String("greenRedGain"),   m_settings.greenRedGain);
216     action.addParameter(QLatin1String("redBlueGain"),    m_settings.redBlueGain);
217     action.addParameter(QLatin1String("redGreenGain"),   m_settings.redGreenGain);
218     action.addParameter(QLatin1String("redRedGain"),     m_settings.redRedGain);
219 
220     return action;
221 }
222 
readParameters(const Digikam::FilterAction & action)223 void MixerFilter::readParameters(const Digikam::FilterAction& action)
224 {
225     m_settings.blackBlueGain  = action.parameter(QLatin1String("blackBlueGain")).toDouble();
226     m_settings.blackGreenGain = action.parameter(QLatin1String("blackGreenGain")).toDouble();
227     m_settings.blackRedGain   = action.parameter(QLatin1String("blackRedGain")).toDouble();
228     m_settings.blueBlueGain   = action.parameter(QLatin1String("blueBlueGain")).toDouble();
229     m_settings.blueGreenGain  = action.parameter(QLatin1String("blueGreenGain")).toDouble();
230     m_settings.blueRedGain    = action.parameter(QLatin1String("blueRedGain")).toDouble();
231     m_settings.bMonochrome    = action.parameter(QLatin1String("bMonochrome")).toBool();
232     m_settings.bPreserveLum   = action.parameter(QLatin1String("bPreserveLum")).toBool();
233     m_settings.greenBlueGain  = action.parameter(QLatin1String("greenBlueGain")).toDouble();
234     m_settings.greenGreenGain = action.parameter(QLatin1String("greenGreenGain")).toDouble();
235     m_settings.greenRedGain   = action.parameter(QLatin1String("greenRedGain")).toDouble();
236     m_settings.redBlueGain    = action.parameter(QLatin1String("redBlueGain")).toDouble();
237     m_settings.redGreenGain   = action.parameter(QLatin1String("redGreenGain")).toDouble();
238     m_settings.redRedGain     = action.parameter(QLatin1String("redRedGain")).toDouble();
239 }
240 
241 } // namespace Digikam
242