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