FETurbulence const and inline cleanup
[WebKit-https.git] / Source / WebCore / platform / graphics / filters / FETurbulence.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
4  * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
6  * Copyright (C) 2010 Renata Hodovan <reni@inf.u-szeged.hu>
7  * Copyright (C) 2011 Gabor Loki <loki@webkit.org>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24
25 #include "config.h"
26 #include "FETurbulence.h"
27
28 #include "Filter.h"
29 #include <wtf/text/TextStream.h>
30
31 #include <runtime/Uint8ClampedArray.h>
32 #include <wtf/MathExtras.h>
33 #include <wtf/ParallelJobs.h>
34
35 namespace WebCore {
36
37 /*
38     Produces results in the range [1, 2**31 - 2]. Algorithm is:
39     r = (a * r) mod m where a = randAmplitude = 16807 and
40     m = randMaximum = 2**31 - 1 = 2147483647, r = seed.
41     See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
42     To test: the algorithm should produce the result 1043618065
43     as the 10,000th generated number if the original seed is 1.
44 */
45 static const int s_perlinNoise = 4096;
46 static const long s_randMaximum = 2147483647; // 2**31 - 1
47 static const int s_randAmplitude = 16807; // 7**5; primitive root of m
48 static const int s_randQ = 127773; // m / a
49 static const int s_randR = 2836; // m % a
50
51 FETurbulence::FETurbulence(Filter& filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
52     : FilterEffect(filter)
53     , m_type(type)
54     , m_baseFrequencyX(baseFrequencyX)
55     , m_baseFrequencyY(baseFrequencyY)
56     , m_numOctaves(numOctaves)
57     , m_seed(seed)
58     , m_stitchTiles(stitchTiles)
59 {
60 }
61
62 Ref<FETurbulence> FETurbulence::create(Filter& filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
63 {
64     return adoptRef(*new FETurbulence(filter, type, baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles));
65 }
66
67 TurbulenceType FETurbulence::type() const
68 {
69     return m_type;
70 }
71
72 bool FETurbulence::setType(TurbulenceType type)
73 {
74     if (m_type == type)
75         return false;
76     m_type = type;
77     return true;
78 }
79
80 float FETurbulence::baseFrequencyY() const
81 {
82     return m_baseFrequencyY;
83 }
84
85 bool FETurbulence::setBaseFrequencyY(float baseFrequencyY)
86 {
87     if (m_baseFrequencyY == baseFrequencyY)
88         return false;
89     m_baseFrequencyY = baseFrequencyY;
90     return true;
91 }
92
93 float FETurbulence::baseFrequencyX() const
94 {
95     return m_baseFrequencyX;
96 }
97
98 bool FETurbulence::setBaseFrequencyX(float baseFrequencyX)
99 {
100     if (m_baseFrequencyX == baseFrequencyX)
101         return false;
102     m_baseFrequencyX = baseFrequencyX;
103     return true;
104 }
105
106 float FETurbulence::seed() const
107 {
108     return m_seed; 
109 }
110
111 bool FETurbulence::setSeed(float seed)
112 {
113     if (m_seed == seed)
114         return false;
115     m_seed = seed;
116     return true;
117 }
118
119 int FETurbulence::numOctaves() const
120 {
121     return m_numOctaves;
122 }
123
124 bool FETurbulence::setNumOctaves(int numOctaves)
125 {
126     if (m_numOctaves == numOctaves)
127         return false;
128     m_numOctaves = numOctaves;
129     return true;
130 }
131
132 bool FETurbulence::stitchTiles() const
133 {
134     return m_stitchTiles;
135 }
136
137 bool FETurbulence::setStitchTiles(bool stitch)
138 {
139     if (m_stitchTiles == stitch)
140         return false;
141     m_stitchTiles = stitch;
142     return true;
143 }
144
145 // The turbulence calculation code is an adapted version of what appears in the SVG 1.1 specification:
146 // http://www.w3.org/TR/SVG11/filters.html#feTurbulence
147
148 // Compute pseudo random number.
149 inline long FETurbulence::PaintingData::random()
150 {
151     long result = s_randAmplitude * (seed % s_randQ) - s_randR * (seed / s_randQ);
152     if (result <= 0)
153         result += s_randMaximum;
154     seed = result;
155     return result;
156 }
157
158 inline float smoothCurve(float t)
159 {
160     return t * t * (3 - 2 * t);
161 }
162
163 inline float linearInterpolation(float t, float a, float b)
164 {
165     return a + t * (b - a);
166 }
167
168 void FETurbulence::initPaint(PaintingData& paintingData)
169 {
170     float normalizationFactor;
171
172     // The seed value clamp to the range [1, s_randMaximum - 1].
173     if (paintingData.seed <= 0)
174         paintingData.seed = -(paintingData.seed % (s_randMaximum - 1)) + 1;
175     if (paintingData.seed > s_randMaximum - 1)
176         paintingData.seed = s_randMaximum - 1;
177
178     float* gradient;
179     for (int channel = 0; channel < 4; ++channel) {
180         for (int i = 0; i < s_blockSize; ++i) {
181             paintingData.latticeSelector[i] = i;
182             gradient = paintingData.gradient[channel][i];
183             do {
184                 gradient[0] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
185                 gradient[1] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
186             } while (!gradient[0] && !gradient[1]);
187             normalizationFactor = sqrtf(gradient[0] * gradient[0] + gradient[1] * gradient[1]);
188             gradient[0] /= normalizationFactor;
189             gradient[1] /= normalizationFactor;
190         }
191     }
192
193     for (int i = s_blockSize - 1; i > 0; --i) {
194         int k = paintingData.latticeSelector[i];
195         int j = paintingData.random() % s_blockSize;
196         ASSERT(j >= 0);
197         ASSERT(j < 2 * s_blockSize + 2);
198         paintingData.latticeSelector[i] = paintingData.latticeSelector[j];
199         paintingData.latticeSelector[j] = k;
200     }
201
202     for (int i = 0; i < s_blockSize + 2; ++i) {
203         paintingData.latticeSelector[s_blockSize + i] = paintingData.latticeSelector[i];
204         for (int channel = 0; channel < 4; ++channel) {
205             paintingData.gradient[channel][s_blockSize + i][0] = paintingData.gradient[channel][i][0];
206             paintingData.gradient[channel][s_blockSize + i][1] = paintingData.gradient[channel][i][1];
207         }
208     }
209 }
210
211 inline void checkNoise(int& noiseValue, int limitValue, int newValue)
212 {
213     if (noiseValue >= limitValue)
214         noiseValue -= newValue;
215     if (noiseValue >= limitValue - 1)
216         noiseValue -= newValue - 1;
217 }
218
219 float FETurbulence::noise2D(int channel, const PaintingData& paintingData, StitchData& stitchData, const FloatPoint& noiseVector)
220 {
221     struct Noise {
222         int noisePositionIntegerValue;
223         float noisePositionFractionValue;
224
225         Noise(float component)
226         {
227             float position = component + s_perlinNoise;
228             noisePositionIntegerValue = static_cast<int>(position);
229             noisePositionFractionValue = position - noisePositionIntegerValue;
230         }
231     };
232
233     Noise noiseX(noiseVector.x());
234     Noise noiseY(noiseVector.y());
235
236     // If stitching, adjust lattice points accordingly.
237     if (m_stitchTiles) {
238         checkNoise(noiseX.noisePositionIntegerValue, stitchData.wrapX, stitchData.width);
239         checkNoise(noiseY.noisePositionIntegerValue, stitchData.wrapY, stitchData.height);
240     }
241
242     noiseX.noisePositionIntegerValue &= s_blockMask;
243     noiseY.noisePositionIntegerValue &= s_blockMask;
244     int latticeIndex = paintingData.latticeSelector[noiseX.noisePositionIntegerValue];
245     int nextLatticeIndex = paintingData.latticeSelector[(noiseX.noisePositionIntegerValue + 1) & s_blockMask];
246
247     float sx = smoothCurve(noiseX.noisePositionFractionValue);
248     float sy = smoothCurve(noiseY.noisePositionFractionValue);
249     float a, b, u, v;
250
251     // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement.
252     int temp = paintingData.latticeSelector[latticeIndex + noiseY.noisePositionIntegerValue];
253     const float* q = paintingData.gradient[channel][temp];
254     u = noiseX.noisePositionFractionValue * q[0] + noiseY.noisePositionFractionValue * q[1];
255     temp = paintingData.latticeSelector[nextLatticeIndex + noiseY.noisePositionIntegerValue];
256     q = paintingData.gradient[channel][temp];
257     v = (noiseX.noisePositionFractionValue - 1) * q[0] + noiseY.noisePositionFractionValue * q[1];
258     a = linearInterpolation(sx, u, v);
259     temp = paintingData.latticeSelector[latticeIndex + noiseY.noisePositionIntegerValue + 1];
260     q = paintingData.gradient[channel][temp];
261     u = noiseX.noisePositionFractionValue * q[0] + (noiseY.noisePositionFractionValue - 1) * q[1];
262     temp = paintingData.latticeSelector[nextLatticeIndex + noiseY.noisePositionIntegerValue + 1];
263     q = paintingData.gradient[channel][temp];
264     v = (noiseX.noisePositionFractionValue - 1) * q[0] + (noiseY.noisePositionFractionValue - 1) * q[1];
265     b = linearInterpolation(sx, u, v);
266     return linearInterpolation(sy, a, b);
267 }
268
269 unsigned char FETurbulence::calculateTurbulenceValueForPoint(int channel, const PaintingData& paintingData, StitchData& stitchData, const FloatPoint& point)
270 {
271     float tileWidth = paintingData.filterSize.width();
272     float tileHeight = paintingData.filterSize.height();
273     ASSERT(tileWidth > 0 && tileHeight > 0);
274     float baseFrequencyX = m_baseFrequencyX;
275     float baseFrequencyY = m_baseFrequencyY;
276     // Adjust the base frequencies if necessary for stitching.
277     if (m_stitchTiles) {
278         // When stitching tiled turbulence, the frequencies must be adjusted
279         // so that the tile borders will be continuous.
280         if (baseFrequencyX) {
281             float lowFrequency = floorf(tileWidth * baseFrequencyX) / tileWidth;
282             float highFrequency = ceilf(tileWidth * baseFrequencyX) / tileWidth;
283             // BaseFrequency should be non-negative according to the standard.
284             if (baseFrequencyX / lowFrequency < highFrequency / baseFrequencyX)
285                 baseFrequencyX = lowFrequency;
286             else
287                 baseFrequencyX = highFrequency;
288         }
289         if (baseFrequencyY) {
290             float lowFrequency = floorf(tileHeight * baseFrequencyY) / tileHeight;
291             float highFrequency = ceilf(tileHeight * baseFrequencyY) / tileHeight;
292             if (baseFrequencyY / lowFrequency < highFrequency / baseFrequencyY)
293                 baseFrequencyY = lowFrequency;
294             else
295                 baseFrequencyY = highFrequency;
296         }
297         // Set up TurbulenceInitial stitch values.
298         stitchData.width = roundf(tileWidth * baseFrequencyX);
299         stitchData.wrapX = s_perlinNoise + stitchData.width;
300         stitchData.height = roundf(tileHeight * baseFrequencyY);
301         stitchData.wrapY = s_perlinNoise + stitchData.height;
302     }
303
304     float turbulenceFunctionResult = 0;
305     FloatPoint noiseVector(point.x() * baseFrequencyX, point.y() * baseFrequencyY);
306     float ratio = 1;
307     for (int octave = 0; octave < m_numOctaves; ++octave) {
308         if (m_type == FETURBULENCE_TYPE_FRACTALNOISE)
309             turbulenceFunctionResult += noise2D(channel, paintingData, stitchData, noiseVector) / ratio;
310         else
311             turbulenceFunctionResult += fabsf(noise2D(channel, paintingData, stitchData, noiseVector)) / ratio;
312         noiseVector.setX(noiseVector.x() * 2);
313         noiseVector.setY(noiseVector.y() * 2);
314         ratio *= 2;
315         if (m_stitchTiles) {
316             // Update stitch values. Subtracting s_perlinNoiseoise before the multiplication and
317             // adding it afterward simplifies to subtracting it once.
318             stitchData.width *= 2;
319             stitchData.wrapX = 2 * stitchData.wrapX - s_perlinNoise;
320             stitchData.height *= 2;
321             stitchData.wrapY = 2 * stitchData.wrapY - s_perlinNoise;
322         }
323     }
324
325     // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult * 255) + 255) / 2 by fractalNoise
326     // and (turbulenceFunctionResult * 255) by turbulence.
327     if (m_type == FETURBULENCE_TYPE_FRACTALNOISE)
328         turbulenceFunctionResult = turbulenceFunctionResult * 0.5f + 0.5f;
329     // Clamp result
330     turbulenceFunctionResult = std::max(std::min(turbulenceFunctionResult, 1.f), 0.f);
331     return static_cast<unsigned char>(turbulenceFunctionResult * 255);
332 }
333
334 void FETurbulence::fillRegion(Uint8ClampedArray* pixelArray, const PaintingData& paintingData, int startY, int endY)
335 {
336     IntRect filterRegion = absolutePaintRect();
337     IntPoint point(0, filterRegion.y() + startY);
338     int indexOfPixelChannel = startY * (filterRegion.width() << 2);
339     int channel;
340     StitchData stitchData;
341
342     for (int y = startY; y < endY; ++y) {
343         point.setY(point.y() + 1);
344         point.setX(filterRegion.x());
345         for (int x = 0; x < filterRegion.width(); ++x) {
346             point.setX(point.x() + 1);
347             for (channel = 0; channel < 4; ++channel, ++indexOfPixelChannel)
348                 pixelArray->set(indexOfPixelChannel, calculateTurbulenceValueForPoint(channel, paintingData, stitchData, filter().mapAbsolutePointToLocalPoint(point)));
349         }
350     }
351 }
352
353 void FETurbulence::fillRegionWorker(FillRegionParameters* parameters)
354 {
355     parameters->filter->fillRegion(parameters->pixelArray, *parameters->paintingData, parameters->startY, parameters->endY);
356 }
357
358 void FETurbulence::platformApplySoftware()
359 {
360     Uint8ClampedArray* pixelArray = createUnmultipliedImageResult();
361     if (!pixelArray)
362         return;
363
364     if (absolutePaintRect().isEmpty()) {
365         pixelArray->zeroFill();
366         return;
367     }
368
369     PaintingData paintingData(m_seed, roundedIntSize(filterPrimitiveSubregion().size()));
370     initPaint(paintingData);
371
372     int optimalThreadNumber = (absolutePaintRect().width() * absolutePaintRect().height()) / s_minimalRectDimension;
373     if (optimalThreadNumber > 1) {
374         // Initialize parallel jobs
375         WTF::ParallelJobs<FillRegionParameters> parallelJobs(&WebCore::FETurbulence::fillRegionWorker, optimalThreadNumber);
376
377         // Fill the parameter array
378         int i = parallelJobs.numberOfJobs();
379         if (i > 1) {
380             // Split the job into "stepY"-sized jobs but there a few jobs that need to be slightly larger since
381             // stepY * jobs < total size. These extras are handled by the remainder "jobsWithExtra".
382             const int stepY = absolutePaintRect().height() / i;
383             const int jobsWithExtra = absolutePaintRect().height() % i;
384
385             int startY = 0;
386             for (; i > 0; --i) {
387                 FillRegionParameters& params = parallelJobs.parameter(i-1);
388                 params.filter = this;
389                 params.pixelArray = pixelArray;
390                 params.paintingData = &paintingData;
391                 params.startY = startY;
392                 startY += i < jobsWithExtra ? stepY + 1 : stepY;
393                 params.endY = startY;
394             }
395
396             // Execute parallel jobs
397             parallelJobs.execute();
398             return;
399         }
400     }
401
402     // Fallback to single threaded mode if there is no room for a new thread or the paint area is too small.
403     fillRegion(pixelArray, paintingData, 0, absolutePaintRect().height());
404 }
405
406 void FETurbulence::dump()
407 {
408 }
409
410 static TextStream& operator<<(TextStream& ts, const TurbulenceType& type)
411 {
412     switch (type) {
413     case FETURBULENCE_TYPE_UNKNOWN:
414         ts << "UNKNOWN";
415         break;
416     case FETURBULENCE_TYPE_TURBULENCE:
417         ts << "TURBULANCE";
418         break;
419     case FETURBULENCE_TYPE_FRACTALNOISE:
420         ts << "NOISE";
421         break;
422     }
423     return ts;
424 }
425
426 TextStream& FETurbulence::externalRepresentation(TextStream& ts, int indent) const
427 {
428     writeIndent(ts, indent);
429     ts << "[feTurbulence";
430     FilterEffect::externalRepresentation(ts);
431     ts << " type=\"" << type() << "\" "
432        << "baseFrequency=\"" << baseFrequencyX() << ", " << baseFrequencyY() << "\" "
433        << "seed=\"" << seed() << "\" "
434        << "numOctaves=\"" << numOctaves() << "\" "
435        << "stitchTiles=\"" << stitchTiles() << "\"]\n";
436     return ts;
437 }
438
439 } // namespace WebCore