1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2005-05-25
7 * Description : TextureFilter threaded image filter.
8 *
9 * Copyright (C) 2005-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10 * Copyright (C) 2006-2010 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
11 * Copyright (C) 2010 by Martin Klapetek <martin dot klapetek at gmail dot com>
12 *
13 * This program is free software; you can redistribute it
14 * and/or modify it under the terms of the GNU General
15 * Public License as published by the Free Software Foundation;
16 * either version 2, or (at your option)
17 * any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * ============================================================ */
25
26 #include "texturefilter.h"
27
28 // C++ includes
29
30 #include <cmath>
31 #include <cstdlib>
32
33 // KDE includes
34
35 #include <klocalizedstring.h>
36
37 // Local includes
38
39 #include "dimg.h"
40 #include "digikam_debug.h"
41 #include "digikam_globals.h"
42
43 namespace Digikam
44 {
45
TextureFilter(QObject * const parent)46 TextureFilter::TextureFilter(QObject* const parent)
47 : DImgThreadedFilter(parent)
48 {
49 initFilter();
50 }
51
TextureFilter(DImg * const orgImage,QObject * const parent,const TextureContainer & settings)52 TextureFilter::TextureFilter(DImg* const orgImage, QObject* const parent, const TextureContainer& settings)
53 : DImgThreadedFilter(orgImage, parent, QLatin1String("Texture")),
54 m_settings (settings)
55 {
56 initFilter();
57 }
58
~TextureFilter()59 TextureFilter::~TextureFilter()
60 {
61 cancelFilter();
62 }
63
DisplayableName()64 QString TextureFilter::DisplayableName()
65 {
66 return QString::fromUtf8(I18N_NOOP("Texture Filter"));
67 }
68
69 /**
70 * This method is based on the Simulate Texture Film tutorial from GimpGuru.org web site
71 * available at this url : www.gimpguru.org/Tutorials/SimulatedTexture/
72 */
73
74 //#define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ( ( (t >> 8) + t ) >> 8))
75
intMult8(uint a,uint b)76 inline static int intMult8(uint a, uint b)
77 {
78 uint t = a * b + 0x80;
79
80 return (((t >> 8) + t) >> 8);
81 }
82
intMult16(uint a,uint b)83 inline static int intMult16(uint a, uint b)
84 {
85 uint t = a * b + 0x8000;
86
87 return (((t >> 16) + t) >> 16);
88 }
89
filterImage()90 void TextureFilter::filterImage()
91 {
92 // Texture tile.
93
94 int w = m_orgImage.width();
95 int h = m_orgImage.height();
96 int bytesDepth = m_orgImage.bytesDepth();
97 bool sixteenBit = m_orgImage.sixteenBit();
98 QString texturePath = TextureContainer::getTexturePath(m_settings.textureType);
99
100 qCDebug(DIGIKAM_DIMG_LOG) << "Texture File: " << texturePath;
101 DImg texture(texturePath);
102
103 if (texture.isNull())
104 {
105 return;
106 }
107
108 DImg textureImg(w, h, m_orgImage.sixteenBit(), m_orgImage.hasAlpha());
109
110 texture.convertToDepthOfImage(&textureImg);
111
112 for (int x = 0 ; x < w ; x += texture.width())
113 {
114 for (int y = 0 ; y < h ; y += texture.height())
115 {
116 textureImg.bitBltImage(&texture, x, y);
117 }
118 }
119
120 // Apply texture.
121
122 uchar* data = m_orgImage.bits();
123 uchar* pTeData = textureImg.bits();
124 uchar* pOutBits = m_destImage.bits();
125 uint offset;
126
127 DColor teData, transData, inData, outData;
128 uchar* ptr = nullptr, *dptr = nullptr, *tptr = nullptr;
129 int progress;
130
131 int blendGain;
132
133 if (sixteenBit)
134 {
135 blendGain = (m_settings.blendGain + 1) * 256 - 1;
136 }
137 else
138 {
139 blendGain = m_settings.blendGain;
140 }
141
142 // Make textured transparent layout.
143
144 for (int x = 0 ; runningFlag() && x < w ; ++x)
145 {
146 for (int y = 0 ; runningFlag() && y < h ; ++y)
147 {
148 offset = x * bytesDepth + (y * w * bytesDepth);
149 ptr = data + offset;
150 tptr = pTeData + offset;
151
152 (void)ptr; // Remove clang warnings.
153
154 // Read color
155
156 teData.setColor(tptr, sixteenBit);
157
158 // in the old algorithm, this was
159 //teData.channel.red = (teData.channel.red * (255 - m_blendGain) +
160 // transData.channel.red * m_setting.blendGain) >> 8;
161 // but transdata was uninitialized, its components were apparently 0,
162 // so I removed the part after the "+".
163
164 if (sixteenBit)
165 {
166 teData.blendInvAlpha16(blendGain);
167 }
168 else
169 {
170 teData.blendInvAlpha8(blendGain);
171 }
172
173 // Overwrite RGB.
174
175 teData.setPixel(tptr);
176 }
177
178 // Update progress bar in dialog.
179
180 progress = (int)(((double) x * 50.0) / w);
181
182 if ((progress % 5) == 0)
183 {
184 postProgress(progress);
185 }
186 }
187
188 // Merge layout and image using overlay method.
189
190 for (int x = 0 ; runningFlag() && x < w ; ++x)
191 {
192 for (int y = 0 ; runningFlag() && y < h ; ++y)
193 {
194 offset = x * bytesDepth + (y * w * bytesDepth);
195 ptr = data + offset;
196 dptr = pOutBits + offset;
197 tptr = pTeData + offset;
198
199 inData.setColor(ptr, sixteenBit);
200 outData.setColor(dptr, sixteenBit);
201 teData.setColor(tptr, sixteenBit);
202
203 if (sixteenBit)
204 {
205 outData.setRed(intMult16(inData.red(), inData.red() + intMult16(2 * teData.red(), 65535 - inData.red())));
206 outData.setGreen(intMult16(inData.green(), inData.green() + intMult16(2 * teData.green(), 65535 - inData.green())));
207 outData.setBlue(intMult16(inData.blue(), inData.blue() + intMult16(2 * teData.blue(), 65535 - inData.blue())));
208 }
209 else
210 {
211 outData.setRed(intMult8(inData.red(), inData.red() + intMult8(2 * teData.red(), 255 - inData.red())));
212 outData.setGreen(intMult8(inData.green(), inData.green() + intMult8(2 * teData.green(), 255 - inData.green())));
213 outData.setBlue(intMult8(inData.blue(), inData.blue() + intMult8(2 * teData.blue(), 255 - inData.blue())));
214 }
215
216 outData.setAlpha(inData.alpha());
217 outData.setPixel(dptr);
218 }
219
220 // Update progress bar in dialog.
221 progress = (int)(50.0 + ((double) x * 50.0) / w);
222
223 if ((progress % 5) == 0)
224 {
225 postProgress(progress);
226 }
227 }
228 }
229
filterAction()230 FilterAction TextureFilter::filterAction()
231 {
232 FilterAction action(FilterIdentifier(), CurrentVersion());
233 action.setDisplayableName(DisplayableName());
234
235 action.addParameter(QLatin1String("blendGain"), m_settings.blendGain);
236 action.addParameter(QLatin1String("textureType"), m_settings.textureType);
237
238 return action;
239 }
240
readParameters(const Digikam::FilterAction & action)241 void TextureFilter::readParameters(const Digikam::FilterAction& action)
242 {
243 m_settings.blendGain = action.parameter(QLatin1String("blendGain")).toInt();
244 m_settings.textureType = action.parameter(QLatin1String("textureType")).toInt();
245 }
246
247 } // namespace Digikam
248