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  * Copyright (C) 2011 Gabor Loki <loki (at) webkit.org>
      8  * Copyright (C) 2013 Google Inc. All rights reserved.
      9  *
     10  * This library is free software; you can redistribute it and/or
     11  * modify it under the terms of the GNU Library General Public
     12  * License as published by the Free Software Foundation; either
     13  * version 2 of the License, or (at your option) any later version.
     14  *
     15  * This library is distributed in the hope that it will be useful,
     16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     18  * Library General Public License for more details.
     19  *
     20  * You should have received a copy of the GNU Library General Public License
     21  * along with this library; see the file COPYING.LIB.  If not, write to
     22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     23  * Boston, MA 02110-1301, USA.
     24  */
     25 
     26 #include "config.h"
     27 
     28 #include "core/platform/graphics/filters/FETurbulence.h"
     29 #include "SkPerlinNoiseShader.h"
     30 #include "SkRectShaderImageFilter.h"
     31 #include "core/platform/graphics/filters/Filter.h"
     32 #include "core/platform/text/TextStream.h"
     33 #include "core/rendering/RenderTreeAsText.h"
     34 
     35 #include "wtf/MathExtras.h"
     36 #include "wtf/ParallelJobs.h"
     37 #include "wtf/Uint8ClampedArray.h"
     38 
     39 namespace WebCore {
     40 
     41 /*
     42     Produces results in the range [1, 2**31 - 2]. Algorithm is:
     43     r = (a * r) mod m where a = randAmplitude = 16807 and
     44     m = randMaximum = 2**31 - 1 = 2147483647, r = seed.
     45     See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
     46     To test: the algorithm should produce the result 1043618065
     47     as the 10,000th generated number if the original seed is 1.
     48 */
     49 static const int s_perlinNoise = 4096;
     50 static const long s_randMaximum = 2147483647; // 2**31 - 1
     51 static const int s_randAmplitude = 16807; // 7**5; primitive root of m
     52 static const int s_randQ = 127773; // m / a
     53 static const int s_randR = 2836; // m % a
     54 
     55 FETurbulence::FETurbulence(Filter* filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
     56     : FilterEffect(filter)
     57     , m_type(type)
     58     , m_baseFrequencyX(baseFrequencyX)
     59     , m_baseFrequencyY(baseFrequencyY)
     60     , m_numOctaves(numOctaves)
     61     , m_seed(seed)
     62     , m_stitchTiles(stitchTiles)
     63 {
     64 }
     65 
     66 PassRefPtr<FETurbulence> FETurbulence::create(Filter* filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
     67 {
     68     return adoptRef(new FETurbulence(filter, type, baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles));
     69 }
     70 
     71 TurbulenceType FETurbulence::type() const
     72 {
     73     return m_type;
     74 }
     75 
     76 bool FETurbulence::setType(TurbulenceType type)
     77 {
     78     if (m_type == type)
     79         return false;
     80     m_type = type;
     81     return true;
     82 }
     83 
     84 float FETurbulence::baseFrequencyY() const
     85 {
     86     return m_baseFrequencyY;
     87 }
     88 
     89 bool FETurbulence::setBaseFrequencyY(float baseFrequencyY)
     90 {
     91     if (m_baseFrequencyY == baseFrequencyY)
     92         return false;
     93     m_baseFrequencyY = baseFrequencyY;
     94     return true;
     95 }
     96 
     97 float FETurbulence::baseFrequencyX() const
     98 {
     99     return m_baseFrequencyX;
    100 }
    101 
    102 bool FETurbulence::setBaseFrequencyX(float baseFrequencyX)
    103 {
    104     if (m_baseFrequencyX == baseFrequencyX)
    105         return false;
    106     m_baseFrequencyX = baseFrequencyX;
    107     return true;
    108 }
    109 
    110 float FETurbulence::seed() const
    111 {
    112     return m_seed;
    113 }
    114 
    115 bool FETurbulence::setSeed(float seed)
    116 {
    117     if (m_seed == seed)
    118         return false;
    119     m_seed = seed;
    120     return true;
    121 }
    122 
    123 int FETurbulence::numOctaves() const
    124 {
    125     return m_numOctaves;
    126 }
    127 
    128 bool FETurbulence::setNumOctaves(int numOctaves)
    129 {
    130     if (m_numOctaves == numOctaves)
    131         return false;
    132     m_numOctaves = numOctaves;
    133     return true;
    134 }
    135 
    136 bool FETurbulence::stitchTiles() const
    137 {
    138     return m_stitchTiles;
    139 }
    140 
    141 bool FETurbulence::setStitchTiles(bool stitch)
    142 {
    143     if (m_stitchTiles == stitch)
    144         return false;
    145     m_stitchTiles = stitch;
    146     return true;
    147 }
    148 
    149 // The turbulence calculation code is an adapted version of what appears in the SVG 1.1 specification:
    150 // http://www.w3.org/TR/SVG11/filters.html#feTurbulence
    151 
    152 // Compute pseudo random number.
    153 inline long FETurbulence::PaintingData::random()
    154 {
    155     long result = s_randAmplitude * (seed % s_randQ) - s_randR * (seed / s_randQ);
    156     if (result <= 0)
    157         result += s_randMaximum;
    158     seed = result;
    159     return result;
    160 }
    161 
    162 inline float smoothCurve(float t)
    163 {
    164     return t * t * (3 - 2 * t);
    165 }
    166 
    167 inline float linearInterpolation(float t, float a, float b)
    168 {
    169     return a + t * (b - a);
    170 }
    171 
    172 inline void FETurbulence::initPaint(PaintingData& paintingData)
    173 {
    174     float normalizationFactor;
    175 
    176     // The seed value clamp to the range [1, s_randMaximum - 1].
    177     if (paintingData.seed <= 0)
    178         paintingData.seed = -(paintingData.seed % (s_randMaximum - 1)) + 1;
    179     if (paintingData.seed > s_randMaximum - 1)
    180         paintingData.seed = s_randMaximum - 1;
    181 
    182     float* gradient;
    183     for (int channel = 0; channel < 4; ++channel) {
    184         for (int i = 0; i < s_blockSize; ++i) {
    185             paintingData.latticeSelector[i] = i;
    186             gradient = paintingData.gradient[channel][i];
    187             gradient[0] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
    188             gradient[1] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
    189             normalizationFactor = sqrtf(gradient[0] * gradient[0] + gradient[1] * gradient[1]);
    190             gradient[0] /= normalizationFactor;
    191             gradient[1] /= normalizationFactor;
    192         }
    193     }
    194     for (int i = s_blockSize - 1; i > 0; --i) {
    195         int k = paintingData.latticeSelector[i];
    196         int j = paintingData.random() % s_blockSize;
    197         ASSERT(j >= 0);
    198         ASSERT(j < 2 * s_blockSize + 2);
    199         paintingData.latticeSelector[i] = paintingData.latticeSelector[j];
    200         paintingData.latticeSelector[j] = k;
    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, 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     float* q;
    236     float sx, sy, a, b, u, v;
    237 
    238     // If stitching, adjust lattice points accordingly.
    239     if (m_stitchTiles) {
    240         checkNoise(noiseX.noisePositionIntegerValue, stitchData.wrapX, stitchData.width);
    241         checkNoise(noiseY.noisePositionIntegerValue, stitchData.wrapY, stitchData.height);
    242     }
    243 
    244     noiseX.noisePositionIntegerValue &= s_blockMask;
    245     noiseY.noisePositionIntegerValue &= s_blockMask;
    246     int latticeIndex = paintingData.latticeSelector[noiseX.noisePositionIntegerValue];
    247     int nextLatticeIndex = paintingData.latticeSelector[(noiseX.noisePositionIntegerValue + 1) & s_blockMask];
    248 
    249     sx = smoothCurve(noiseX.noisePositionFractionValue);
    250     sy = smoothCurve(noiseY.noisePositionFractionValue);
    251 
    252     // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement.
    253     int temp = paintingData.latticeSelector[latticeIndex + noiseY.noisePositionIntegerValue];
    254     q = paintingData.gradient[channel][temp];
    255     u = noiseX.noisePositionFractionValue * q[0] + noiseY.noisePositionFractionValue * q[1];
    256     temp = paintingData.latticeSelector[nextLatticeIndex + noiseY.noisePositionIntegerValue];
    257     q = paintingData.gradient[channel][temp];
    258     v = (noiseX.noisePositionFractionValue - 1) * q[0] + noiseY.noisePositionFractionValue * q[1];
    259     a = linearInterpolation(sx, u, v);
    260     temp = paintingData.latticeSelector[latticeIndex + noiseY.noisePositionIntegerValue + 1];
    261     q = paintingData.gradient[channel][temp];
    262     u = noiseX.noisePositionFractionValue * q[0] + (noiseY.noisePositionFractionValue - 1) * q[1];
    263     temp = paintingData.latticeSelector[nextLatticeIndex + noiseY.noisePositionIntegerValue + 1];
    264     q = paintingData.gradient[channel][temp];
    265     v = (noiseX.noisePositionFractionValue - 1) * q[0] + (noiseY.noisePositionFractionValue - 1) * q[1];
    266     b = linearInterpolation(sx, u, v);
    267     return linearInterpolation(sy, a, b);
    268 }
    269 
    270 unsigned char FETurbulence::calculateTurbulenceValueForPoint(int channel, PaintingData& paintingData, StitchData& stitchData, const FloatPoint& point, float baseFrequencyX, float baseFrequencyY)
    271 {
    272     float tileWidth = paintingData.filterSize.width();
    273     float tileHeight = paintingData.filterSize.height();
    274     ASSERT(tileWidth > 0 && tileHeight > 0);
    275     // Adjust the base frequencies if necessary for stitching.
    276     if (m_stitchTiles) {
    277         // When stitching tiled turbulence, the frequencies must be adjusted
    278         // so that the tile borders will be continuous.
    279         if (baseFrequencyX) {
    280             float lowFrequency = floorf(tileWidth * baseFrequencyX) / tileWidth;
    281             float highFrequency = ceilf(tileWidth * baseFrequencyX) / tileWidth;
    282             // BaseFrequency should be non-negative according to the standard.
    283             if (baseFrequencyX / lowFrequency < highFrequency / baseFrequencyX)
    284                 baseFrequencyX = lowFrequency;
    285             else
    286                 baseFrequencyX = highFrequency;
    287         }
    288         if (baseFrequencyY) {
    289             float lowFrequency = floorf(tileHeight * baseFrequencyY) / tileHeight;
    290             float highFrequency = ceilf(tileHeight * baseFrequencyY) / tileHeight;
    291             if (baseFrequencyY / lowFrequency < highFrequency / baseFrequencyY)
    292                 baseFrequencyY = lowFrequency;
    293             else
    294                 baseFrequencyY = highFrequency;
    295         }
    296         // Set up TurbulenceInitial stitch values.
    297         stitchData.width = roundf(tileWidth * baseFrequencyX);
    298         stitchData.wrapX = s_perlinNoise + stitchData.width;
    299         stitchData.height = roundf(tileHeight * baseFrequencyY);
    300         stitchData.wrapY = s_perlinNoise + stitchData.height;
    301     }
    302     float turbulenceFunctionResult = 0;
    303     FloatPoint noiseVector(point.x() * baseFrequencyX, point.y() * baseFrequencyY);
    304     float ratio = 1;
    305     for (int octave = 0; octave < m_numOctaves; ++octave) {
    306         if (m_type == FETURBULENCE_TYPE_FRACTALNOISE)
    307             turbulenceFunctionResult += noise2D(channel, paintingData, stitchData, noiseVector) / ratio;
    308         else
    309             turbulenceFunctionResult += fabsf(noise2D(channel, paintingData, stitchData, noiseVector)) / ratio;
    310         noiseVector.setX(noiseVector.x() * 2);
    311         noiseVector.setY(noiseVector.y() * 2);
    312         ratio *= 2;
    313         if (m_stitchTiles) {
    314             // Update stitch values. Subtracting s_perlinNoiseoise before the multiplication and
    315             // adding it afterward simplifies to subtracting it once.
    316             stitchData.width *= 2;
    317             stitchData.wrapX = 2 * stitchData.wrapX - s_perlinNoise;
    318             stitchData.height *= 2;
    319             stitchData.wrapY = 2 * stitchData.wrapY - s_perlinNoise;
    320         }
    321     }
    322 
    323     // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult * 255) + 255) / 2 by fractalNoise
    324     // and (turbulenceFunctionResult * 255) by turbulence.
    325     if (m_type == FETURBULENCE_TYPE_FRACTALNOISE)
    326         turbulenceFunctionResult = turbulenceFunctionResult * 0.5f + 0.5f;
    327     // Clamp result
    328     turbulenceFunctionResult = std::max(std::min(turbulenceFunctionResult, 1.f), 0.f);
    329     return static_cast<unsigned char>(turbulenceFunctionResult * 255);
    330 }
    331 
    332 inline void FETurbulence::fillRegion(Uint8ClampedArray* pixelArray, PaintingData& paintingData, int startY, int endY, float baseFrequencyX, float baseFrequencyY)
    333 {
    334     IntRect filterRegion = absolutePaintRect();
    335     IntPoint point(0, filterRegion.y() + startY);
    336     int indexOfPixelChannel = startY * (filterRegion.width() << 2);
    337     int channel;
    338     StitchData stitchData;
    339 
    340     for (int y = startY; y < endY; ++y) {
    341         point.setY(point.y() + 1);
    342         point.setX(filterRegion.x());
    343         for (int x = 0; x < filterRegion.width(); ++x) {
    344             point.setX(point.x() + 1);
    345             for (channel = 0; channel < 4; ++channel, ++indexOfPixelChannel)
    346                 pixelArray->set(indexOfPixelChannel, calculateTurbulenceValueForPoint(channel, paintingData, stitchData, filter()->mapAbsolutePointToLocalPoint(point), baseFrequencyX, baseFrequencyY));
    347         }
    348     }
    349 }
    350 
    351 void FETurbulence::fillRegionWorker(FillRegionParameters* parameters)
    352 {
    353     parameters->filter->fillRegion(parameters->pixelArray, *parameters->paintingData, parameters->startY, parameters->endY, parameters->baseFrequencyX, parameters->baseFrequencyY);
    354 }
    355 
    356 void FETurbulence::applySoftware()
    357 {
    358     Uint8ClampedArray* pixelArray = createUnmultipliedImageResult();
    359     if (!pixelArray)
    360         return;
    361 
    362     if (absolutePaintRect().isEmpty()) {
    363         pixelArray->zeroFill();
    364         return;
    365     }
    366 
    367     PaintingData paintingData(m_seed, roundedIntSize(filterPrimitiveSubregion().size()));
    368     initPaint(paintingData);
    369     float baseFrequencyX = 1.0f / filter()->applyHorizontalScale(1.0f / m_baseFrequencyX);
    370     float baseFrequencyY = 1.0f / filter()->applyVerticalScale(1.0f / m_baseFrequencyY);
    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                 params.baseFrequencyX = baseFrequencyX;
    395                 params.baseFrequencyY = baseFrequencyY;
    396             }
    397 
    398             // Execute parallel jobs
    399             parallelJobs.execute();
    400             return;
    401         }
    402     }
    403 
    404     // Fallback to single threaded mode if there is no room for a new thread or the paint area is too small.
    405     fillRegion(pixelArray, paintingData, 0, absolutePaintRect().height(), baseFrequencyX, baseFrequencyY);
    406 }
    407 
    408 SkShader* FETurbulence::createShader(const IntRect& filterRegion)
    409 {
    410     const SkISize size = SkISize::Make(filterRegion.width(), filterRegion.height());
    411     float baseFrequencyX = 1.0f / filter()->applyHorizontalScale(1.0f / m_baseFrequencyX);
    412     const float baseFrequencyY = 1.0f / filter()->applyVerticalScale(1.0f / m_baseFrequencyY);
    413     return (type() == FETURBULENCE_TYPE_FRACTALNOISE) ?
    414         SkPerlinNoiseShader::CreateFractalNoise(SkFloatToScalar(baseFrequencyX),
    415             SkFloatToScalar(baseFrequencyY), numOctaves(), SkFloatToScalar(seed()),
    416             stitchTiles() ? &size : 0) :
    417         SkPerlinNoiseShader::CreateTubulence(SkFloatToScalar(baseFrequencyX),
    418             SkFloatToScalar(baseFrequencyY), numOctaves(), SkFloatToScalar(seed()),
    419             stitchTiles() ? &size : 0);
    420 }
    421 
    422 bool FETurbulence::applySkia()
    423 {
    424     // For now, only use the skia implementation for accelerated rendering.
    425     if (filter()->renderingMode() != Accelerated)
    426         return false;
    427 
    428     ImageBuffer* resultImage = createImageBufferResult();
    429     if (!resultImage)
    430         return false;
    431 
    432     const IntRect filterRegion = absolutePaintRect();
    433 
    434     SkPaint paint;
    435     paint.setShader(createShader(filterRegion))->unref();
    436     resultImage->context()->drawRect((SkRect)filterRegion, paint);
    437     return true;
    438 }
    439 
    440 PassRefPtr<SkImageFilter> FETurbulence::createImageFilter(SkiaImageFilterBuilder* builder)
    441 {
    442     SkAutoTUnref<SkShader> shader(createShader(IntRect()));
    443     return adoptRef(SkRectShaderImageFilter::Create(shader, SkRect()));
    444 }
    445 
    446 static TextStream& operator<<(TextStream& ts, const TurbulenceType& type)
    447 {
    448     switch (type) {
    449     case FETURBULENCE_TYPE_UNKNOWN:
    450         ts << "UNKNOWN";
    451         break;
    452     case FETURBULENCE_TYPE_TURBULENCE:
    453         ts << "TURBULENCE";
    454         break;
    455     case FETURBULENCE_TYPE_FRACTALNOISE:
    456         ts << "NOISE";
    457         break;
    458     }
    459     return ts;
    460 }
    461 
    462 TextStream& FETurbulence::externalRepresentation(TextStream& ts, int indent) const
    463 {
    464     writeIndent(ts, indent);
    465     ts << "[feTurbulence";
    466     FilterEffect::externalRepresentation(ts);
    467     ts << " type=\"" << type() << "\" "
    468        << "baseFrequency=\"" << baseFrequencyX() << ", " << baseFrequencyY() << "\" "
    469        << "seed=\"" << seed() << "\" "
    470        << "numOctaves=\"" << numOctaves() << "\" "
    471        << "stitchTiles=\"" << stitchTiles() << "\"]\n";
    472     return ts;
    473 }
    474 
    475 } // namespace WebCore
    476