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