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