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