1/*
2 * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
3 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
4 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
5 * Copyright (C) 2012 University of Szeged
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB.  If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include "config.h"
24
25#if ENABLE(FILTERS)
26#include "FilterEffect.h"
27
28#include "Filter.h"
29#include "ImageBuffer.h"
30#include "TextStream.h"
31#include <runtime/JSCInlines.h>
32#include <runtime/TypedArrayInlines.h>
33#include <runtime/Uint8ClampedArray.h>
34
35#if HAVE(ARM_NEON_INTRINSICS)
36#include <arm_neon.h>
37#endif
38
39namespace WebCore {
40
41static const float kMaxFilterArea = 4096 * 4096;
42
43FilterEffect::FilterEffect(Filter* filter)
44    : m_alphaImage(false)
45    , m_filter(filter)
46    , m_hasX(false)
47    , m_hasY(false)
48    , m_hasWidth(false)
49    , m_hasHeight(false)
50    , m_clipsToBounds(true)
51    , m_operatingColorSpace(ColorSpaceLinearRGB)
52    , m_resultColorSpace(ColorSpaceDeviceRGB)
53{
54    ASSERT(m_filter);
55}
56
57FilterEffect::~FilterEffect()
58{
59}
60
61float FilterEffect::maxFilterArea()
62{
63    return kMaxFilterArea;
64}
65
66bool FilterEffect::isFilterSizeValid(const FloatRect& rect)
67{
68    if (rect.width() < 0 || rect.height() < 0
69        || (rect.height() * rect.width() > kMaxFilterArea))
70        return false;
71    return true;
72}
73
74void FilterEffect::determineAbsolutePaintRect()
75{
76    m_absolutePaintRect = IntRect();
77    unsigned size = m_inputEffects.size();
78    for (unsigned i = 0; i < size; ++i)
79        m_absolutePaintRect.unite(m_inputEffects.at(i)->absolutePaintRect());
80
81    // Filters in SVG clip to primitive subregion, while CSS doesn't.
82    if (m_clipsToBounds)
83        m_absolutePaintRect.intersect(enclosingIntRect(m_maxEffectRect));
84    else
85        m_absolutePaintRect.unite(enclosingIntRect(m_maxEffectRect));
86
87}
88
89IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
90{
91    ASSERT(hasResult());
92    IntPoint location = m_absolutePaintRect.location();
93    location.moveBy(-effectRect.location());
94    return IntRect(location, m_absolutePaintRect.size());
95}
96
97FloatRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
98{
99    return FloatRect(FloatPoint(srcRect.x() - m_absolutePaintRect.x(),
100                            srcRect.y() - m_absolutePaintRect.y()), srcRect.size());
101}
102
103FilterEffect* FilterEffect::inputEffect(unsigned number) const
104{
105    ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size());
106    return m_inputEffects.at(number).get();
107}
108
109#if ENABLE(OPENCL)
110void FilterEffect::applyAll()
111{
112    if (hasResult())
113        return;
114    FilterContextOpenCL* context = FilterContextOpenCL::context();
115    if (context) {
116        apply();
117        if (!context->inError())
118            return;
119        clearResultsRecursive();
120        context->destroyContext();
121    }
122    // Software code path.
123    apply();
124}
125#endif
126
127void FilterEffect::apply()
128{
129    if (hasResult())
130        return;
131    unsigned size = m_inputEffects.size();
132    for (unsigned i = 0; i < size; ++i) {
133        FilterEffect* in = m_inputEffects.at(i).get();
134        in->apply();
135        if (!in->hasResult())
136            return;
137
138        // Convert input results to the current effect's color space.
139        transformResultColorSpace(in, i);
140    }
141
142    determineAbsolutePaintRect();
143    setResultColorSpace(m_operatingColorSpace);
144
145    if (!isFilterSizeValid(m_absolutePaintRect))
146        return;
147
148    if (requiresValidPreMultipliedPixels()) {
149        for (unsigned i = 0; i < size; ++i)
150            inputEffect(i)->correctFilterResultIfNeeded();
151    }
152
153    // Add platform specific apply functions here and return earlier.
154#if ENABLE(OPENCL)
155    if (platformApplyOpenCL())
156        return;
157#endif
158    platformApplySoftware();
159}
160
161#if ENABLE(OPENCL)
162// This function will be changed to abstract virtual when all filters are landed.
163bool FilterEffect::platformApplyOpenCL()
164{
165    if (!FilterContextOpenCL::context())
166        return false;
167
168    unsigned size = m_inputEffects.size();
169    for (unsigned i = 0; i < size; ++i) {
170        FilterEffect* in = m_inputEffects.at(i).get();
171        // Software code path expects that at least one of the following fileds is valid.
172        if (!in->m_imageBufferResult && !in->m_unmultipliedImageResult && !in->m_premultipliedImageResult)
173            in->asImageBuffer();
174    }
175
176    platformApplySoftware();
177    ImageBuffer* sourceImage = asImageBuffer();
178    if (sourceImage) {
179        RefPtr<Uint8ClampedArray> sourceImageData = sourceImage->getUnmultipliedImageData(IntRect(IntPoint(), sourceImage->internalSize()));
180        createOpenCLImageResult(sourceImageData->data());
181    }
182    return true;
183}
184#endif
185
186void FilterEffect::forceValidPreMultipliedPixels()
187{
188    // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
189    if (!m_premultipliedImageResult)
190        return;
191
192    Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
193    unsigned char* pixelData = imageArray->data();
194    int pixelArrayLength = imageArray->length();
195
196    // We must have four bytes per pixel, and complete pixels
197    ASSERT(!(pixelArrayLength % 4));
198
199#if HAVE(ARM_NEON_INTRINSICS)
200    if (pixelArrayLength >= 64) {
201        unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
202        do {
203            // Increments pixelData by 64.
204            uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
205            sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
206            sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
207            sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
208            vst4q_u8(pixelData, sixteenPixels);
209            pixelData += 64;
210        } while (pixelData < lastPixel);
211
212        pixelArrayLength &= 0x3f;
213        if (!pixelArrayLength)
214            return;
215    }
216#endif
217
218    int numPixels = pixelArrayLength / 4;
219
220    // Iterate over each pixel, checking alpha and adjusting color components if necessary
221    while (--numPixels >= 0) {
222        // Alpha is the 4th byte in a pixel
223        unsigned char a = *(pixelData + 3);
224        // Clamp each component to alpha, and increment the pixel location
225        for (int i = 0; i < 3; ++i) {
226            if (*pixelData > a)
227                *pixelData = a;
228            ++pixelData;
229        }
230        // Increment for alpha
231        ++pixelData;
232    }
233}
234
235void FilterEffect::clearResult()
236{
237    if (m_imageBufferResult)
238        m_imageBufferResult.reset();
239    if (m_unmultipliedImageResult)
240        m_unmultipliedImageResult.clear();
241    if (m_premultipliedImageResult)
242        m_premultipliedImageResult.clear();
243#if ENABLE(OPENCL)
244    if (m_openCLImageResult)
245        m_openCLImageResult.clear();
246#endif
247}
248
249void FilterEffect::clearResultsRecursive()
250{
251    // Clear all results, regardless that the current effect has
252    // a result. Can be used if an effect is in an erroneous state.
253    if (hasResult())
254        clearResult();
255
256    unsigned size = m_inputEffects.size();
257    for (unsigned i = 0; i < size; ++i)
258        m_inputEffects.at(i).get()->clearResultsRecursive();
259}
260
261ImageBuffer* FilterEffect::asImageBuffer()
262{
263    if (!hasResult())
264        return 0;
265    if (m_imageBufferResult)
266        return m_imageBufferResult.get();
267#if ENABLE(OPENCL)
268    if (m_openCLImageResult)
269        return openCLImageToImageBuffer();
270#endif
271    m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter->filterScale(), m_resultColorSpace, m_filter->renderingMode());
272    IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
273    if (m_premultipliedImageResult)
274        m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
275    else
276        m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
277    return m_imageBufferResult.get();
278}
279
280#if ENABLE(OPENCL)
281ImageBuffer* FilterEffect::openCLImageToImageBuffer()
282{
283    FilterContextOpenCL* context = FilterContextOpenCL::context();
284    ASSERT(context);
285
286    if (context->inError())
287        return 0;
288
289    size_t origin[3] = { 0, 0, 0 };
290    size_t region[3] = { m_absolutePaintRect.width(), m_absolutePaintRect.height(), 1 };
291
292    RefPtr<Uint8ClampedArray> destinationPixelArray = Uint8ClampedArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
293
294    if (context->isFailed(clFinish(context->commandQueue())))
295        return 0;
296
297    if (context->isFailed(clEnqueueReadImage(context->commandQueue(), m_openCLImageResult, CL_TRUE, origin, region, 0, 0, destinationPixelArray->data(), 0, 0, 0)))
298        return 0;
299
300    m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size());
301    IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
302    m_imageBufferResult->putByteArray(Unmultiplied, destinationPixelArray.get(), destinationRect.size(), destinationRect, IntPoint());
303
304    return m_imageBufferResult.get();
305}
306#endif
307
308PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
309{
310    ASSERT(isFilterSizeValid(rect));
311    IntSize scaledSize(rect.size());
312    scaledSize.scale(m_filter->filterScale());
313    RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(scaledSize.width() * scaledSize.height() * 4);
314    copyUnmultipliedImage(imageData.get(), rect);
315    return imageData.release();
316}
317
318PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
319{
320    ASSERT(isFilterSizeValid(rect));
321    IntSize scaledSize(rect.size());
322    scaledSize.scale(m_filter->filterScale());
323    RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(scaledSize.width() * scaledSize.height() * 4);
324    copyPremultipliedImage(imageData.get(), rect);
325    return imageData.release();
326}
327
328inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
329{
330    IntRect scaledRect(rect);
331    scaledRect.scale(m_filter->filterScale());
332    IntSize scaledPaintSize(m_absolutePaintRect.size());
333    scaledPaintSize.scale(m_filter->filterScale());
334
335    // Initialize the destination to transparent black, if not entirely covered by the source.
336    if (scaledRect.x() < 0 || scaledRect.y() < 0 || scaledRect.maxX() > scaledPaintSize.width() || scaledRect.maxY() > scaledPaintSize.height())
337        memset(destination->data(), 0, destination->length());
338
339    // Early return if the rect does not intersect with the source.
340    if (scaledRect.maxX() <= 0 || scaledRect.maxY() <= 0 || scaledRect.x() >= scaledPaintSize.width() || scaledRect.y() >= scaledPaintSize.height())
341        return;
342
343    int xOrigin = scaledRect.x();
344    int xDest = 0;
345    if (xOrigin < 0) {
346        xDest = -xOrigin;
347        xOrigin = 0;
348    }
349    int xEnd = scaledRect.maxX();
350    if (xEnd > scaledPaintSize.width())
351        xEnd = scaledPaintSize.width();
352
353    int yOrigin = scaledRect.y();
354    int yDest = 0;
355    if (yOrigin < 0) {
356        yDest = -yOrigin;
357        yOrigin = 0;
358    }
359    int yEnd = scaledRect.maxY();
360    if (yEnd > scaledPaintSize.height())
361        yEnd = scaledPaintSize.height();
362
363    int size = (xEnd - xOrigin) * 4;
364    int destinationScanline = scaledRect.width() * 4;
365    int sourceScanline = scaledPaintSize.width() * 4;
366    unsigned char *destinationPixel = destination->data() + ((yDest * scaledRect.width()) + xDest) * 4;
367    unsigned char *sourcePixel = source->data() + ((yOrigin * scaledPaintSize.width()) + xOrigin) * 4;
368
369    while (yOrigin < yEnd) {
370        memcpy(destinationPixel, sourcePixel, size);
371        destinationPixel += destinationScanline;
372        sourcePixel += sourceScanline;
373        ++yOrigin;
374    }
375}
376
377void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
378{
379    ASSERT(hasResult());
380
381    if (!m_unmultipliedImageResult) {
382        // We prefer a conversion from the image buffer.
383        if (m_imageBufferResult)
384            m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
385        else {
386            ASSERT(isFilterSizeValid(m_absolutePaintRect));
387            IntSize inputSize(m_absolutePaintRect.size());
388            inputSize.scale(m_filter->filterScale());
389            m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(inputSize.width() * inputSize.height() * 4);
390            unsigned char* sourceComponent = m_premultipliedImageResult->data();
391            unsigned char* destinationComponent = m_unmultipliedImageResult->data();
392            unsigned char* end = sourceComponent + (inputSize.width() * inputSize.height() * 4);
393            while (sourceComponent < end) {
394                int alpha = sourceComponent[3];
395                if (alpha) {
396                    destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
397                    destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
398                    destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
399                } else {
400                    destinationComponent[0] = 0;
401                    destinationComponent[1] = 0;
402                    destinationComponent[2] = 0;
403                }
404                destinationComponent[3] = alpha;
405                sourceComponent += 4;
406                destinationComponent += 4;
407            }
408        }
409    }
410    copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
411}
412
413void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
414{
415    ASSERT(hasResult());
416
417    if (!m_premultipliedImageResult) {
418        // We prefer a conversion from the image buffer.
419        if (m_imageBufferResult)
420            m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
421        else {
422            ASSERT(isFilterSizeValid(m_absolutePaintRect));
423            IntSize inputSize(m_absolutePaintRect.size());
424            inputSize.scale(m_filter->filterScale());
425            m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(inputSize.width() * inputSize.height() * 4);
426            unsigned char* sourceComponent = m_unmultipliedImageResult->data();
427            unsigned char* destinationComponent = m_premultipliedImageResult->data();
428            unsigned char* end = sourceComponent + (inputSize.width() * inputSize.height() * 4);
429            while (sourceComponent < end) {
430                int alpha = sourceComponent[3];
431                destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
432                destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
433                destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
434                destinationComponent[3] = alpha;
435                sourceComponent += 4;
436                destinationComponent += 4;
437            }
438        }
439    }
440    copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
441}
442
443ImageBuffer* FilterEffect::createImageBufferResult()
444{
445    // Only one result type is allowed.
446    ASSERT(!hasResult());
447    if (m_absolutePaintRect.isEmpty())
448        return 0;
449    m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter->filterScale(), m_resultColorSpace, m_filter->renderingMode());
450    if (!m_imageBufferResult)
451        return 0;
452    ASSERT(m_imageBufferResult->context());
453    return m_imageBufferResult.get();
454}
455
456Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
457{
458    // Only one result type is allowed.
459    ASSERT(!hasResult());
460    ASSERT(isFilterSizeValid(m_absolutePaintRect));
461
462    if (m_absolutePaintRect.isEmpty())
463        return 0;
464    IntSize resultSize(m_absolutePaintRect.size());
465    resultSize.scale(m_filter->filterScale());
466    m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(resultSize.width() * resultSize.height() * 4);
467    return m_unmultipliedImageResult.get();
468}
469
470Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
471{
472    // Only one result type is allowed.
473    ASSERT(!hasResult());
474    ASSERT(isFilterSizeValid(m_absolutePaintRect));
475
476    if (m_absolutePaintRect.isEmpty())
477        return 0;
478    IntSize resultSize(m_absolutePaintRect.size());
479    resultSize.scale(m_filter->filterScale());
480    m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(resultSize.width() * resultSize.height() * 4);
481    return m_premultipliedImageResult.get();
482}
483
484#if ENABLE(OPENCL)
485OpenCLHandle FilterEffect::createOpenCLImageResult(uint8_t* source)
486{
487    FilterContextOpenCL* context = FilterContextOpenCL::context();
488    ASSERT(context);
489
490    if (context->inError())
491        return 0;
492
493    ASSERT(!hasResult());
494    cl_image_format clImageFormat;
495    clImageFormat.image_channel_order = CL_RGBA;
496    clImageFormat.image_channel_data_type = CL_UNORM_INT8;
497
498    int errorCode = 0;
499#ifdef CL_API_SUFFIX__VERSION_1_2
500    cl_image_desc imageDescriptor = { CL_MEM_OBJECT_IMAGE2D, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, 0, 0, 0, 0, 0, 0};
501    m_openCLImageResult = clCreateImage(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
502        &clImageFormat, &imageDescriptor, source, &errorCode);
503#else
504    m_openCLImageResult = clCreateImage2D(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
505        &clImageFormat, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, source, &errorCode);
506#endif
507    if (context->isFailed(errorCode))
508        return 0;
509
510    return m_openCLImageResult;
511}
512#endif
513
514void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
515{
516#if USE(CG)
517    // CG handles color space adjustments internally.
518    UNUSED_PARAM(dstColorSpace);
519#else
520    if (!hasResult() || dstColorSpace == m_resultColorSpace)
521        return;
522
523#if ENABLE(OPENCL)
524    if (openCLImage()) {
525        if (m_imageBufferResult)
526            m_imageBufferResult.clear();
527        FilterContextOpenCL* context = FilterContextOpenCL::context();
528        ASSERT(context);
529        context->openCLTransformColorSpace(m_openCLImageResult, absolutePaintRect(), m_resultColorSpace, dstColorSpace);
530    } else {
531#endif
532
533        // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
534        // color space transform support for the {pre,un}multiplied arrays.
535        asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
536
537#if ENABLE(OPENCL)
538    }
539#endif
540
541    m_resultColorSpace = dstColorSpace;
542
543    if (m_unmultipliedImageResult)
544        m_unmultipliedImageResult.clear();
545    if (m_premultipliedImageResult)
546        m_premultipliedImageResult.clear();
547#endif
548}
549
550TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
551{
552    // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
553    // possible at the moment, because we need more detailed informations from the target object.
554    return ts;
555}
556
557} // namespace WebCore
558
559#endif // ENABLE(FILTERS)
560