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