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