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