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 #include "platform/graphics/filters/FETurbulence.h"
     28 
     29 #include "SkPerlinNoiseShader.h"
     30 #include "SkRectShaderImageFilter.h"
     31 #include "platform/graphics/filters/ParallelJobs.h"
     32 #include "platform/graphics/filters/SkiaImageFilterBuilder.h"
     33 #include "platform/text/TextStream.h"
     34 #include "wtf/MathExtras.h"
     35 #include "wtf/Uint8ClampedArray.h"
     36 
     37 namespace WebCore {
     38 
     39 /*
     40     Produces results in the range [1, 2**31 - 2]. Algorithm is:
     41     r = (a * r) mod m where a = randAmplitude = 16807 and
     42     m = randMaximum = 2**31 - 1 = 2147483647, r = seed.
     43     See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
     44     To test: the algorithm should produce the result 1043618065
     45     as the 10,000th generated number if the original seed is 1.
     46 */
     47 static const int s_perlinNoise = 4096;
     48 static const long s_randMaximum = 2147483647; // 2**31 - 1
     49 static const int s_randAmplitude = 16807; // 7**5; primitive root of m
     50 static const int s_randQ = 127773; // m / a
     51 static const int s_randR = 2836; // m % a
     52 
     53 FETurbulence::FETurbulence(Filter* filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
     54     : FilterEffect(filter)
     55     , m_type(type)
     56     , m_baseFrequencyX(baseFrequencyX)
     57     , m_baseFrequencyY(baseFrequencyY)
     58     , m_numOctaves(numOctaves)
     59     , m_seed(seed)
     60     , m_stitchTiles(stitchTiles)
     61 {
     62 }
     63 
     64 PassRefPtr<FETurbulence> FETurbulence::create(Filter* filter, TurbulenceType type, float baseFrequencyX, float baseFrequencyY, int numOctaves, float seed, bool stitchTiles)
     65 {
     66     return adoptRef(new FETurbulence(filter, type, baseFrequencyX, baseFrequencyY, numOctaves, seed, stitchTiles));
     67 }
     68 
     69 TurbulenceType FETurbulence::type() const
     70 {
     71     return m_type;
     72 }
     73 
     74 bool FETurbulence::setType(TurbulenceType type)
     75 {
     76     if (m_type == type)
     77         return false;
     78     m_type = type;
     79     return true;
     80 }
     81 
     82 float FETurbulence::baseFrequencyY() const
     83 {
     84     return m_baseFrequencyY;
     85 }
     86 
     87 bool FETurbulence::setBaseFrequencyY(float baseFrequencyY)
     88 {
     89     if (m_baseFrequencyY == baseFrequencyY)
     90         return false;
     91     m_baseFrequencyY = baseFrequencyY;
     92     return true;
     93 }
     94 
     95 float FETurbulence::baseFrequencyX() const
     96 {
     97     return m_baseFrequencyX;
     98 }
     99 
    100 bool FETurbulence::setBaseFrequencyX(float baseFrequencyX)
    101 {
    102     if (m_baseFrequencyX == baseFrequencyX)
    103         return false;
    104     m_baseFrequencyX = baseFrequencyX;
    105     return true;
    106 }
    107 
    108 float FETurbulence::seed() const
    109 {
    110     return m_seed;
    111 }
    112 
    113 bool FETurbulence::setSeed(float seed)
    114 {
    115     if (m_seed == seed)
    116         return false;
    117     m_seed = seed;
    118     return true;
    119 }
    120 
    121 int FETurbulence::numOctaves() const
    122 {
    123     return m_numOctaves;
    124 }
    125 
    126 bool FETurbulence::setNumOctaves(int numOctaves)
    127 {
    128     if (m_numOctaves == numOctaves)
    129         return false;
    130     m_numOctaves = numOctaves;
    131     return true;
    132 }
    133 
    134 bool FETurbulence::stitchTiles() const
    135 {
    136     return m_stitchTiles;
    137 }
    138 
    139 bool FETurbulence::setStitchTiles(bool stitch)
    140 {
    141     if (m_stitchTiles == stitch)
    142         return false;
    143     m_stitchTiles = stitch;
    144     return true;
    145 }
    146 
    147 // The turbulence calculation code is an adapted version of what appears in the SVG 1.1 specification:
    148 // http://www.w3.org/TR/SVG11/filters.html#feTurbulence
    149 
    150 // Compute pseudo random number.
    151 inline long FETurbulence::PaintingData::random()
    152 {
    153     long result = s_randAmplitude * (seed % s_randQ) - s_randR * (seed / s_randQ);
    154     if (result <= 0)
    155         result += s_randMaximum;
    156     seed = result;
    157     return result;
    158 }
    159 
    160 inline float smoothCurve(float t)
    161 {
    162     return t * t * (3 - 2 * t);
    163 }
    164 
    165 inline float linearInterpolation(float t, float a, float b)
    166 {
    167     return a + t * (b - a);
    168 }
    169 
    170 inline void FETurbulence::initPaint(PaintingData& paintingData)
    171 {
    172     float normalizationFactor;
    173 
    174     // The seed value clamp to the range [1, s_randMaximum - 1].
    175     if (paintingData.seed <= 0)
    176         paintingData.seed = -(paintingData.seed % (s_randMaximum - 1)) + 1;
    177     if (paintingData.seed > s_randMaximum - 1)
    178         paintingData.seed = s_randMaximum - 1;
    179 
    180     float* gradient;
    181     for (int channel = 0; channel < 4; ++channel) {
    182         for (int i = 0; i < s_blockSize; ++i) {
    183             paintingData.latticeSelector[i] = i;
    184             gradient = paintingData.gradient[channel][i];
    185             gradient[0] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
    186             gradient[1] = static_cast<float>((paintingData.random() % (2 * s_blockSize)) - s_blockSize) / s_blockSize;
    187             normalizationFactor = sqrtf(gradient[0] * gradient[0] + gradient[1] * gradient[1]);
    188             gradient[0] /= normalizationFactor;
    189             gradient[1] /= normalizationFactor;
    190         }
    191     }
    192     for (int i = s_blockSize - 1; i > 0; --i) {
    193         int k = paintingData.latticeSelector[i];
    194         int j = paintingData.random() % s_blockSize;
    195         ASSERT(j >= 0);
    196         ASSERT(j < 2 * s_blockSize + 2);
    197         paintingData.latticeSelector[i] = paintingData.latticeSelector[j];
    198         paintingData.latticeSelector[j] = k;
    199     }
    200     for (int i = 0; i < s_blockSize + 2; ++i) {
    201         paintingData.latticeSelector[s_blockSize + i] = paintingData.latticeSelector[i];
    202         for (int channel = 0; channel < 4; ++channel) {
    203             paintingData.gradient[channel][s_blockSize + i][0] = paintingData.gradient[channel][i][0];
    204             paintingData.gradient[channel][s_blockSize + i][1] = paintingData.gradient[channel][i][1];
    205         }
    206     }
    207 }
    208 
    209 inline void checkNoise(int& noiseValue, int limitValue, int newValue)
    210 {
    211     if (noiseValue >= limitValue)
    212         noiseValue -= newValue;
    213     if (noiseValue >= limitValue - 1)
    214         noiseValue -= newValue - 1;
    215 }
    216 
    217 float FETurbulence::noise2D(int channel, PaintingData& paintingData, StitchData& stitchData, const FloatPoint& noiseVector)
    218 {
    219     struct Noise {
    220         int noisePositionIntegerValue;
    221         float noisePositionFractionValue;
    222 
    223         Noise(float component)
    224         {
    225             float position = component + s_perlinNoise;
    226             noisePositionIntegerValue = static_cast<int>(position);
    227             noisePositionFractionValue = position - noisePositionIntegerValue;
    228         }
    229     };
    230 
    231     Noise noiseX(noiseVector.x());
    232     Noise noiseY(noiseVector.y());
    233     float* q;
    234     float sx, sy, a, b, u, v;
    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     sx = smoothCurve(noiseX.noisePositionFractionValue);
    248     sy = smoothCurve(noiseY.noisePositionFractionValue);
    249 
    250     // This is taken 1:1 from SVG spec: http://www.w3.org/TR/SVG11/filters.html#feTurbulenceElement.
    251     int temp = paintingData.latticeSelector[latticeIndex + noiseY.noisePositionIntegerValue];
    252     q = paintingData.gradient[channel][temp];
    253     u = noiseX.noisePositionFractionValue * q[0] + noiseY.noisePositionFractionValue * q[1];
    254     temp = paintingData.latticeSelector[nextLatticeIndex + noiseY.noisePositionIntegerValue];
    255     q = paintingData.gradient[channel][temp];
    256     v = (noiseX.noisePositionFractionValue - 1) * q[0] + noiseY.noisePositionFractionValue * q[1];
    257     a = linearInterpolation(sx, u, v);
    258     temp = paintingData.latticeSelector[latticeIndex + noiseY.noisePositionIntegerValue + 1];
    259     q = paintingData.gradient[channel][temp];
    260     u = noiseX.noisePositionFractionValue * q[0] + (noiseY.noisePositionFractionValue - 1) * q[1];
    261     temp = paintingData.latticeSelector[nextLatticeIndex + noiseY.noisePositionIntegerValue + 1];
    262     q = paintingData.gradient[channel][temp];
    263     v = (noiseX.noisePositionFractionValue - 1) * q[0] + (noiseY.noisePositionFractionValue - 1) * q[1];
    264     b = linearInterpolation(sx, u, v);
    265     return linearInterpolation(sy, a, b);
    266 }
    267 
    268 unsigned char FETurbulence::calculateTurbulenceValueForPoint(int channel, PaintingData& paintingData, StitchData& stitchData, const FloatPoint& point, float baseFrequencyX, float baseFrequencyY)
    269 {
    270     float tileWidth = paintingData.filterSize.width();
    271     float tileHeight = paintingData.filterSize.height();
    272     ASSERT(tileWidth > 0 && tileHeight > 0);
    273     // Adjust the base frequencies if necessary for stitching.
    274     if (m_stitchTiles) {
    275         // When stitching tiled turbulence, the frequencies must be adjusted
    276         // so that the tile borders will be continuous.
    277         if (baseFrequencyX) {
    278             float lowFrequency = floorf(tileWidth * baseFrequencyX) / tileWidth;
    279             float highFrequency = ceilf(tileWidth * baseFrequencyX) / tileWidth;
    280             // BaseFrequency should be non-negative according to the standard.
    281             if (baseFrequencyX / lowFrequency < highFrequency / baseFrequencyX)
    282                 baseFrequencyX = lowFrequency;
    283             else
    284                 baseFrequencyX = highFrequency;
    285         }
    286         if (baseFrequencyY) {
    287             float lowFrequency = floorf(tileHeight * baseFrequencyY) / tileHeight;
    288             float highFrequency = ceilf(tileHeight * baseFrequencyY) / tileHeight;
    289             if (baseFrequencyY / lowFrequency < highFrequency / baseFrequencyY)
    290                 baseFrequencyY = lowFrequency;
    291             else
    292                 baseFrequencyY = highFrequency;
    293         }
    294         // Set up TurbulenceInitial stitch values.
    295         stitchData.width = roundf(tileWidth * baseFrequencyX);
    296         stitchData.wrapX = s_perlinNoise + stitchData.width;
    297         stitchData.height = roundf(tileHeight * baseFrequencyY);
    298         stitchData.wrapY = s_perlinNoise + stitchData.height;
    299     }
    300     float turbulenceFunctionResult = 0;
    301     FloatPoint noiseVector(point.x() * baseFrequencyX, point.y() * baseFrequencyY);
    302     float ratio = 1;
    303     for (int octave = 0; octave < m_numOctaves; ++octave) {
    304         if (m_type == FETURBULENCE_TYPE_FRACTALNOISE)
    305             turbulenceFunctionResult += noise2D(channel, paintingData, stitchData, noiseVector) / ratio;
    306         else
    307             turbulenceFunctionResult += fabsf(noise2D(channel, paintingData, stitchData, noiseVector)) / ratio;
    308         noiseVector.setX(noiseVector.x() * 2);
    309         noiseVector.setY(noiseVector.y() * 2);
    310         ratio *= 2;
    311         if (m_stitchTiles) {
    312             // Update stitch values. Subtracting s_perlinNoiseoise before the multiplication and
    313             // adding it afterward simplifies to subtracting it once.
    314             stitchData.width *= 2;
    315             stitchData.wrapX = 2 * stitchData.wrapX - s_perlinNoise;
    316             stitchData.height *= 2;
    317             stitchData.wrapY = 2 * stitchData.wrapY - s_perlinNoise;
    318         }
    319     }
    320 
    321     // The value of turbulenceFunctionResult comes from ((turbulenceFunctionResult * 255) + 255) / 2 by fractalNoise
    322     // and (turbulenceFunctionResult * 255) by turbulence.
    323     if (m_type == FETURBULENCE_TYPE_FRACTALNOISE)
    324         turbulenceFunctionResult = turbulenceFunctionResult * 0.5f + 0.5f;
    325     // Clamp result
    326     turbulenceFunctionResult = std::max(std::min(turbulenceFunctionResult, 1.f), 0.f);
    327     return static_cast<unsigned char>(turbulenceFunctionResult * 255);
    328 }
    329 
    330 inline void FETurbulence::fillRegion(Uint8ClampedArray* pixelArray, PaintingData& paintingData, int startY, int endY, float baseFrequencyX, float baseFrequencyY)
    331 {
    332     IntRect filterRegion = absolutePaintRect();
    333     IntPoint point(0, filterRegion.y() + startY);
    334     int indexOfPixelChannel = startY * (filterRegion.width() << 2);
    335     int channel;
    336     StitchData stitchData;
    337 
    338     for (int y = startY; y < endY; ++y) {
    339         point.setY(point.y() + 1);
    340         point.setX(filterRegion.x());
    341         for (int x = 0; x < filterRegion.width(); ++x) {
    342             point.setX(point.x() + 1);
    343             for (channel = 0; channel < 4; ++channel, ++indexOfPixelChannel)
    344                 pixelArray->set(indexOfPixelChannel, calculateTurbulenceValueForPoint(channel, paintingData, stitchData, filter()->mapAbsolutePointToLocalPoint(point), baseFrequencyX, baseFrequencyY));
    345         }
    346     }
    347 }
    348 
    349 void FETurbulence::fillRegionWorker(FillRegionParameters* parameters)
    350 {
    351     parameters->filter->fillRegion(parameters->pixelArray, *parameters->paintingData, parameters->startY, parameters->endY, parameters->baseFrequencyX, parameters->baseFrequencyY);
    352 }
    353 
    354 void FETurbulence::applySoftware()
    355 {
    356     Uint8ClampedArray* pixelArray = createUnmultipliedImageResult();
    357     if (!pixelArray)
    358         return;
    359 
    360     if (absolutePaintRect().isEmpty()) {
    361         pixelArray->zeroFill();
    362         return;
    363     }
    364 
    365     PaintingData paintingData(m_seed, roundedIntSize(filterPrimitiveSubregion().size()));
    366     initPaint(paintingData);
    367     float baseFrequencyX = 1.0f / filter()->applyHorizontalScale(1.0f / m_baseFrequencyX);
    368     float baseFrequencyY = 1.0f / filter()->applyVerticalScale(1.0f / m_baseFrequencyY);
    369 
    370     int optimalThreadNumber = (absolutePaintRect().width() * absolutePaintRect().height()) / s_minimalRectDimension;
    371     if (optimalThreadNumber > 1) {
    372         // Initialize parallel jobs
    373         ParallelJobs<FillRegionParameters> parallelJobs(&WebCore::FETurbulence::fillRegionWorker, optimalThreadNumber);
    374 
    375         // Fill the parameter array
    376         int i = parallelJobs.numberOfJobs();
    377         if (i > 1) {
    378             // Split the job into "stepY"-sized jobs but there a few jobs that need to be slightly larger since
    379             // stepY * jobs < total size. These extras are handled by the remainder "jobsWithExtra".
    380             const int stepY = absolutePaintRect().height() / i;
    381             const int jobsWithExtra = absolutePaintRect().height() % i;
    382 
    383             int startY = 0;
    384             for (; i > 0; --i) {
    385                 FillRegionParameters& params = parallelJobs.parameter(i-1);
    386                 params.filter = this;
    387                 params.pixelArray = pixelArray;
    388                 params.paintingData = &paintingData;
    389                 params.startY = startY;
    390                 startY += i < jobsWithExtra ? stepY + 1 : stepY;
    391                 params.endY = startY;
    392                 params.baseFrequencyX = baseFrequencyX;
    393                 params.baseFrequencyY = baseFrequencyY;
    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(), baseFrequencyX, baseFrequencyY);
    404 }
    405 
    406 SkShader* FETurbulence::createShader(const IntRect& filterRegion)
    407 {
    408     const SkISize size = SkISize::Make(filterRegion.width(), filterRegion.height());
    409     float baseFrequencyX = 1.0f / filter()->applyHorizontalScale(1.0f / m_baseFrequencyX);
    410     const float baseFrequencyY = 1.0f / filter()->applyVerticalScale(1.0f / m_baseFrequencyY);
    411     return (type() == FETURBULENCE_TYPE_FRACTALNOISE) ?
    412         SkPerlinNoiseShader::CreateFractalNoise(SkFloatToScalar(baseFrequencyX),
    413             SkFloatToScalar(baseFrequencyY), numOctaves(), SkFloatToScalar(seed()),
    414             stitchTiles() ? &size : 0) :
    415         SkPerlinNoiseShader::CreateTubulence(SkFloatToScalar(baseFrequencyX),
    416             SkFloatToScalar(baseFrequencyY), numOctaves(), SkFloatToScalar(seed()),
    417             stitchTiles() ? &size : 0);
    418 }
    419 
    420 bool FETurbulence::applySkia()
    421 {
    422     // For now, only use the skia implementation for accelerated rendering.
    423     if (!filter()->isAccelerated())
    424         return false;
    425 
    426     ImageBuffer* resultImage = createImageBufferResult();
    427     if (!resultImage)
    428         return false;
    429 
    430     const IntRect filterRegion(IntPoint::zero(), absolutePaintRect().size());
    431 
    432     SkPaint paint;
    433     paint.setShader(createShader(filterRegion))->unref();
    434     resultImage->context()->drawRect((SkRect)filterRegion, paint);
    435     return true;
    436 }
    437 
    438 PassRefPtr<SkImageFilter> FETurbulence::createImageFilter(SkiaImageFilterBuilder* builder)
    439 {
    440     SkAutoTUnref<SkShader> shader(createShader(IntRect()));
    441     SkImageFilter::CropRect rect = getCropRect(builder->cropOffset());
    442     return adoptRef(SkRectShaderImageFilter::Create(shader, &rect));
    443 }
    444 
    445 static TextStream& operator<<(TextStream& ts, const TurbulenceType& type)
    446 {
    447     switch (type) {
    448     case FETURBULENCE_TYPE_UNKNOWN:
    449         ts << "UNKNOWN";
    450         break;
    451     case FETURBULENCE_TYPE_TURBULENCE:
    452         ts << "TURBULENCE";
    453         break;
    454     case FETURBULENCE_TYPE_FRACTALNOISE:
    455         ts << "NOISE";
    456         break;
    457     }
    458     return ts;
    459 }
    460 
    461 TextStream& FETurbulence::externalRepresentation(TextStream& ts, int indent) const
    462 {
    463     writeIndent(ts, indent);
    464     ts << "[feTurbulence";
    465     FilterEffect::externalRepresentation(ts);
    466     ts << " type=\"" << type() << "\" "
    467        << "baseFrequency=\"" << baseFrequencyX() << ", " << baseFrequencyY() << "\" "
    468        << "seed=\"" << seed() << "\" "
    469        << "numOctaves=\"" << numOctaves() << "\" "
    470        << "stitchTiles=\"" << stitchTiles() << "\"]\n";
    471     return ts;
    472 }
    473 
    474 } // namespace WebCore
    475