1 /* Webcamoid, webcam capture application.
2  * Copyright (C) 2016  Gonzalo Exequiel Pedone
3  *
4  * Webcamoid is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Webcamoid is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Web-Site: http://webcamoid.github.io/
18  */
19 
20 #include <QVector>
21 #include <QPoint>
22 #include <QImage>
23 #include <QQmlContext>
24 #include <QtMath>
25 #include <akfrac.h>
26 #include <akpacket.h>
27 #include <akvideopacket.h>
28 
29 #include "distortelement.h"
30 
31 class DistortElementPrivate
32 {
33     public:
34         qreal m_amplitude {1.0};
35         qreal m_frequency {1.0};
36         int m_gridSizeLog {1};
37 
38         QPoint plasmaFunction(const QPoint &point, const QSize &size,
39                               qreal amp, qreal freq, qreal t);
40         QVector<QPoint> createGrid(int width, int height,
41                                    int gridSize, qreal time);
42 };
43 
DistortElement()44 DistortElement::DistortElement(): AkElement()
45 {
46     this->d = new DistortElementPrivate;
47 }
48 
~DistortElement()49 DistortElement::~DistortElement()
50 {
51     delete this->d;
52 }
53 
amplitude() const54 qreal DistortElement::amplitude() const
55 {
56     return this->d->m_amplitude;
57 }
58 
frequency() const59 qreal DistortElement::frequency() const
60 {
61     return this->d->m_frequency;
62 }
63 
gridSizeLog() const64 int DistortElement::gridSizeLog() const
65 {
66     return this->d->m_gridSizeLog;
67 }
68 
69 // this will compute a displacement value such that
70 // 0<=x_retval<xsize and 0<=y_retval<ysize.
plasmaFunction(const QPoint & point,const QSize & size,qreal amp,qreal freq,qreal t)71 QPoint DistortElementPrivate::plasmaFunction(const QPoint &point,
72                                              const QSize &size,
73                                              qreal amp,
74                                              qreal freq,
75                                              qreal t)
76 {
77     qreal time = fmod(t, 2 * M_PI);
78     qreal h = size.height() - 1;
79     qreal w = size.width() - 1;
80     qreal dx = (-4.0 / (w * w) * point.x() + 4.0 / w) * point.x();
81     qreal dy = (-4.0 / (h * h) * point.y() + 4.0 / h) * point.y();
82 
83     int x = qRound(point.x() + amp * (size.width() / 4.0) * dx
84                    * sin(freq * point.y() / size.height() + time));
85 
86     int y = qRound(point.y() + amp * (size.height() / 4.0) * dy
87                    * sin(freq * point.x() / size.width() + time));
88 
89     return {qBound(0, x, size.width() - 1), qBound(0, y, size.height() - 1)};
90 }
91 
createGrid(int width,int height,int gridSize,qreal time)92 QVector<QPoint> DistortElementPrivate::createGrid(int width,
93                                                   int height,
94                                                   int gridSize,
95                                                   qreal time)
96 {
97     QVector<QPoint> grid;
98 
99     for (int y = 0; y <= height; y += gridSize)
100         for (int x = 0; x <= width; x += gridSize)
101             grid << this->plasmaFunction(QPoint(x, y), QSize(width, height),
102                                          this->m_amplitude, this->m_frequency,
103                                          time);
104 
105     return grid;
106 }
107 
controlInterfaceProvide(const QString & controlId) const108 QString DistortElement::controlInterfaceProvide(const QString &controlId) const
109 {
110     Q_UNUSED(controlId)
111 
112     return QString("qrc:/Distort/share/qml/main.qml");
113 }
114 
controlInterfaceConfigure(QQmlContext * context,const QString & controlId) const115 void DistortElement::controlInterfaceConfigure(QQmlContext *context,
116                                                const QString &controlId) const
117 {
118     Q_UNUSED(controlId)
119 
120     context->setContextProperty("Distort", const_cast<QObject *>(qobject_cast<const QObject *>(this)));
121     context->setContextProperty("controlId", this->objectName());
122 }
123 
iVideoStream(const AkVideoPacket & packet)124 AkPacket DistortElement::iVideoStream(const AkVideoPacket &packet)
125 {
126     auto src = packet.toImage();
127 
128     if (src.isNull())
129         return AkPacket();
130 
131     src = src.convertToFormat(QImage::Format_ARGB32);
132     QImage oFrame = QImage(src.size(), src.format());
133 
134     const QRgb *srcBits = reinterpret_cast<const QRgb *>(src.constBits());
135     QRgb *destBits = reinterpret_cast<QRgb *>(oFrame.bits());
136 
137     int gridSizeLog = this->d->m_gridSizeLog > 0? this->d->m_gridSizeLog: 1;
138     int gridSize = 1 << gridSizeLog;
139     qreal time = packet.pts() * packet.timeBase().value();
140     auto grid = this->d->createGrid(src.width(), src.height(), gridSize, time);
141 
142     int gridX = src.width() / gridSize;
143     int gridY = src.height() / gridSize;
144 
145     for (int y = 0; y < gridY; y++)
146         for (int x = 0; x < gridX; x++) {
147             int offset = x + y * (gridX + 1);
148 
149             QPoint upperLeft  = grid[offset];
150             QPoint lowerLeft  = grid[offset + gridX + 1];
151             QPoint upperRight = grid[offset + 1];
152             QPoint lowerRight = grid[offset + gridX + 2];
153 
154             int startColXX = upperLeft.x();
155             int startColYY = upperLeft.y();
156             int endColXX = upperRight.x();
157             int endColYY = upperRight.y();
158 
159             int stepStartColX = (lowerLeft.x() - upperLeft.x())
160                                 >> gridSizeLog;
161 
162             int stepStartColY = (lowerLeft.y() - upperLeft.y())
163                                 >> gridSizeLog;
164 
165             int stepEndColX = (lowerRight.x() - upperRight.x())
166                               >> gridSizeLog;
167 
168             int stepEndColY = (lowerRight.y() - upperRight.y())
169                               >> gridSizeLog;
170 
171             int pos = (y << gridSizeLog) * src.width() + (x << gridSizeLog);
172 
173             for (int blockY = 0; blockY < gridSize; blockY++) {
174                 int xLineIndex = startColXX;
175                 int yLineIndex = startColYY;
176 
177                 int stepLineX = (endColXX - startColXX) >> gridSizeLog;
178                 int stepLineY = (endColYY - startColYY) >> gridSizeLog;
179 
180                 for (int i = 0, blockX = 0; blockX < gridSize; i++, blockX++) {
181                     int xx = qBound(0, xLineIndex, src.width() - 1);
182                     int yy = qBound(0, yLineIndex, src.height() - 1);
183 
184                     xLineIndex += stepLineX;
185                     yLineIndex += stepLineY;
186 
187                     destBits[pos + i] = srcBits[xx + yy * src.width()];
188                 }
189 
190                 startColXX += stepStartColX;
191                 endColXX   += stepEndColX;
192                 startColYY += stepStartColY;
193                 endColYY   += stepEndColY;
194 
195                 pos += src.width() - gridSize + gridSize;
196             }
197         }
198 
199     auto oPacket = AkVideoPacket::fromImage(oFrame, packet);
200     akSend(oPacket)
201 }
202 
setAmplitude(qreal amplitude)203 void DistortElement::setAmplitude(qreal amplitude)
204 {
205     if (qFuzzyCompare(this->d->m_amplitude, amplitude))
206         return;
207 
208     this->d->m_amplitude = amplitude;
209     emit this->amplitudeChanged(amplitude);
210 }
211 
setFrequency(qreal frequency)212 void DistortElement::setFrequency(qreal frequency)
213 {
214     if (qFuzzyCompare(this->d->m_frequency, frequency))
215         return;
216 
217     this->d->m_frequency = frequency;
218     emit this->frequencyChanged(frequency);
219 }
220 
setGridSizeLog(int gridSizeLog)221 void DistortElement::setGridSizeLog(int gridSizeLog)
222 {
223     if (this->d->m_gridSizeLog == gridSizeLog)
224         return;
225 
226     this->d->m_gridSizeLog = gridSizeLog;
227     emit this->gridSizeLogChanged(gridSizeLog);
228 }
229 
resetAmplitude()230 void DistortElement::resetAmplitude()
231 {
232     this->setAmplitude(1.0);
233 }
234 
resetFrequency()235 void DistortElement::resetFrequency()
236 {
237     this->setFrequency(1.0);
238 }
239 
resetGridSizeLog()240 void DistortElement::resetGridSizeLog()
241 {
242     this->setGridSizeLog(1);
243 }
244 
245 #include "moc_distortelement.cpp"
246