ba14712a3cf84074e7074ccb8d016d321730343d
[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  * Copyright (C) 2015 Apple Inc. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #include "config.h"
25 #include "FilterEffect.h"
26
27 #include "Filter.h"
28 #include "ImageBuffer.h"
29 #include "TextStream.h"
30 #include <runtime/JSCInlines.h>
31 #include <runtime/TypedArrayInlines.h>
32 #include <runtime/Uint8ClampedArray.h>
33
34 #if HAVE(ARM_NEON_INTRINSICS)
35 #include <arm_neon.h>
36 #endif
37
38 namespace WebCore {
39
40 FilterEffect::FilterEffect(Filter& filter)
41     : m_alphaImage(false)
42     , m_filter(filter)
43     , m_hasX(false)
44     , m_hasY(false)
45     , m_hasWidth(false)
46     , m_hasHeight(false)
47     , m_clipsToBounds(true)
48     , m_operatingColorSpace(ColorSpaceLinearRGB)
49     , m_resultColorSpace(ColorSpaceDeviceRGB)
50 {
51 }
52
53 FilterEffect::~FilterEffect()
54 {
55 }
56
57 void FilterEffect::determineAbsolutePaintRect()
58 {
59     m_absolutePaintRect = IntRect();
60     for (auto& effect : m_inputEffects)
61         m_absolutePaintRect.unite(effect->absolutePaintRect());
62     clipAbsolutePaintRect();
63 }
64
65 void FilterEffect::clipAbsolutePaintRect()
66 {
67     // Filters in SVG clip to primitive subregion, while CSS doesn't.
68     if (m_clipsToBounds)
69         m_absolutePaintRect.intersect(enclosingIntRect(m_maxEffectRect));
70     else
71         m_absolutePaintRect.unite(enclosingIntRect(m_maxEffectRect));
72 }
73
74 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
75 {
76     ASSERT(hasResult());
77     IntPoint location = m_absolutePaintRect.location();
78     location.moveBy(-effectRect.location());
79     return IntRect(location, m_absolutePaintRect.size());
80 }
81
82 FloatRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
83 {
84     ASSERT(hasResult());
85
86     FloatSize scale;
87     ImageBuffer::clampedSize(m_absolutePaintRect.size(), scale);
88
89     AffineTransform transform;
90     transform.scale(scale).translate(-m_absolutePaintRect.location());
91     return transform.mapRect(srcRect);
92 }
93
94 FilterEffect* 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 static unsigned collectEffects(const FilterEffect*effect, HashSet<const FilterEffect*>& allEffects)
101 {
102     allEffects.add(effect);
103     unsigned size = effect->numberOfEffectInputs();
104     for (unsigned i = 0; i < size; ++i) {
105         FilterEffect* in = effect->inputEffect(i);
106         collectEffects(in, allEffects);
107     }
108     return allEffects.size();
109 }
110
111 unsigned FilterEffect::totalNumberOfEffectInputs() const
112 {
113     HashSet<const FilterEffect*> allEffects;
114     return collectEffects(this, allEffects);
115 }
116
117 #if ENABLE(OPENCL)
118 void FilterEffect::applyAll()
119 {
120     if (hasResult())
121         return;
122     FilterContextOpenCL* context = FilterContextOpenCL::context();
123     if (context) {
124         apply();
125         if (!context->inError())
126             return;
127         clearResultsRecursive();
128         context->destroyContext();
129     }
130     // Software code path.
131     apply();
132 }
133 #endif
134
135 void FilterEffect::apply()
136 {
137     if (hasResult())
138         return;
139     unsigned size = m_inputEffects.size();
140     for (unsigned i = 0; i < size; ++i) {
141         FilterEffect* in = m_inputEffects.at(i).get();
142         in->apply();
143         if (!in->hasResult())
144             return;
145
146         // Convert input results to the current effect's color space.
147         transformResultColorSpace(in, i);
148     }
149
150     determineAbsolutePaintRect();
151     setResultColorSpace(m_operatingColorSpace);
152
153     if (m_absolutePaintRect.isEmpty() || ImageBuffer::sizeNeedsClamping(m_absolutePaintRect.size()))
154         return;
155
156     if (requiresValidPreMultipliedPixels()) {
157         for (unsigned i = 0; i < size; ++i)
158             inputEffect(i)->correctFilterResultIfNeeded();
159     }
160     
161     // Add platform specific apply functions here and return earlier.
162 #if ENABLE(OPENCL)
163     if (platformApplyOpenCL())
164         return;
165 #endif
166     platformApplySoftware();
167 }
168
169 #if ENABLE(OPENCL)
170 // This function will be changed to abstract virtual when all filters are landed.
171 bool FilterEffect::platformApplyOpenCL()
172 {
173     if (!FilterContextOpenCL::context())
174         return false;
175
176     unsigned size = m_inputEffects.size();
177     for (unsigned i = 0; i < size; ++i) {
178         FilterEffect* in = m_inputEffects.at(i).get();
179         // Software code path expects that at least one of the following fileds is valid.
180         if (!in->m_imageBufferResult && !in->m_unmultipliedImageResult && !in->m_premultipliedImageResult)
181             in->asImageBuffer();
182     }
183
184     platformApplySoftware();
185     ImageBuffer* sourceImage = asImageBuffer();
186     if (sourceImage) {
187         RefPtr<Uint8ClampedArray> sourceImageData = sourceImage->getUnmultipliedImageData(IntRect(IntPoint(), sourceImage->internalSize()));
188         createOpenCLImageResult(sourceImageData->data());
189     }
190     return true;
191 }
192 #endif
193
194 void FilterEffect::forceValidPreMultipliedPixels()
195 {
196     // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
197     if (!m_premultipliedImageResult)
198         return;
199
200     Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
201     unsigned char* pixelData = imageArray->data();
202     int pixelArrayLength = imageArray->length();
203
204     // We must have four bytes per pixel, and complete pixels
205     ASSERT(!(pixelArrayLength % 4));
206
207 #if HAVE(ARM_NEON_INTRINSICS)
208     if (pixelArrayLength >= 64) {
209         unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
210         do {
211             // Increments pixelData by 64.
212             uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
213             sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
214             sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
215             sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
216             vst4q_u8(pixelData, sixteenPixels);
217             pixelData += 64;
218         } while (pixelData < lastPixel);
219
220         pixelArrayLength &= 0x3f;
221         if (!pixelArrayLength)
222             return;
223     }
224 #endif
225
226     int numPixels = pixelArrayLength / 4;
227
228     // Iterate over each pixel, checking alpha and adjusting color components if necessary
229     while (--numPixels >= 0) {
230         // Alpha is the 4th byte in a pixel
231         unsigned char a = *(pixelData + 3);
232         // Clamp each component to alpha, and increment the pixel location
233         for (int i = 0; i < 3; ++i) {
234             if (*pixelData > a)
235                 *pixelData = a;
236             ++pixelData;
237         }
238         // Increment for alpha
239         ++pixelData;
240     }
241 }
242
243 void FilterEffect::clearResult()
244 {
245     if (m_imageBufferResult)
246         m_imageBufferResult.reset();
247     if (m_unmultipliedImageResult)
248         m_unmultipliedImageResult = nullptr;
249     if (m_premultipliedImageResult)
250         m_premultipliedImageResult = nullptr;
251 #if ENABLE(OPENCL)
252     if (m_openCLImageResult)
253         m_openCLImageResult = nullptr;
254 #endif
255 }
256
257 void FilterEffect::clearResultsRecursive()
258 {
259     // Clear all results, regardless that the current effect has
260     // a result. Can be used if an effect is in an erroneous state.
261     if (hasResult())
262         clearResult();
263
264     unsigned size = m_inputEffects.size();
265     for (unsigned i = 0; i < size; ++i)
266         m_inputEffects.at(i).get()->clearResultsRecursive();
267 }
268
269 ImageBuffer* FilterEffect::asImageBuffer()
270 {
271     if (!hasResult())
272         return nullptr;
273     if (m_imageBufferResult)
274         return m_imageBufferResult.get();
275 #if ENABLE(OPENCL)
276     if (m_openCLImageResult)
277         return openCLImageToImageBuffer();
278 #endif
279     m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter.filterScale(), m_resultColorSpace, m_filter.renderingMode());
280     if (!m_imageBufferResult)
281         return nullptr;
282
283     IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
284     if (m_premultipliedImageResult)
285         m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
286     else
287         m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
288     return m_imageBufferResult.get();
289 }
290
291 #if ENABLE(OPENCL)
292 ImageBuffer* FilterEffect::openCLImageToImageBuffer()
293 {
294     FilterContextOpenCL* context = FilterContextOpenCL::context();
295     ASSERT(context);
296
297     if (context->inError())
298         return nullptr;
299
300     size_t origin[3] = { 0, 0, 0 };
301     size_t region[3] = { m_absolutePaintRect.width(), m_absolutePaintRect.height(), 1 };
302
303     RefPtr<Uint8ClampedArray> destinationPixelArray = Uint8ClampedArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
304
305     if (context->isFailed(clFinish(context->commandQueue())))
306         return nullptr;
307
308     if (context->isFailed(clEnqueueReadImage(context->commandQueue(), m_openCLImageResult, CL_TRUE, origin, region, 0, 0, destinationPixelArray->data(), 0, 0, 0)))
309         return nullptr;
310
311     m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size());
312     if (!m_imageBufferResult)
313         return nullptr;
314
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     IntSize scaledSize(rect.size());
325     ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
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     IntSize scaledSize(rect.size());
335     ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
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             IntSize inputSize(m_absolutePaintRect.size());
401             ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
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             IntSize inputSize(m_absolutePaintRect.size());
437             ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
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 nullptr;
463
464     FloatSize clampedSize = ImageBuffer::clampedSize(m_absolutePaintRect.size());
465     m_imageBufferResult = ImageBuffer::create(clampedSize, m_filter.filterScale(), m_resultColorSpace, m_filter.renderingMode());
466     if (!m_imageBufferResult)
467         return nullptr;
468
469     return m_imageBufferResult.get();
470 }
471
472 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
473 {
474     // Only one result type is allowed.
475     ASSERT(!hasResult());
476     if (m_absolutePaintRect.isEmpty())
477         return nullptr;
478
479     IntSize resultSize(m_absolutePaintRect.size());
480     ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize));
481     resultSize.scale(m_filter.filterScale());
482     m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(resultSize.width() * resultSize.height() * 4);
483     return m_unmultipliedImageResult.get();
484 }
485
486 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
487 {
488     // Only one result type is allowed.
489     ASSERT(!hasResult());
490     if (m_absolutePaintRect.isEmpty())
491         return nullptr;
492
493     IntSize resultSize(m_absolutePaintRect.size());
494     ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize));
495     resultSize.scale(m_filter.filterScale());
496     m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(resultSize.width() * resultSize.height() * 4);
497     return m_premultipliedImageResult.get();
498 }
499
500 #if ENABLE(OPENCL)
501 OpenCLHandle FilterEffect::createOpenCLImageResult(uint8_t* source)
502 {
503     FilterContextOpenCL* context = FilterContextOpenCL::context();
504     ASSERT(context);
505
506     if (context->inError())
507         return 0;
508
509     ASSERT(!hasResult());
510     cl_image_format clImageFormat;
511     clImageFormat.image_channel_order = CL_RGBA;
512     clImageFormat.image_channel_data_type = CL_UNORM_INT8;
513
514     int errorCode = 0;
515 #ifdef CL_API_SUFFIX__VERSION_1_2
516     cl_image_desc imageDescriptor = { CL_MEM_OBJECT_IMAGE2D, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, 0, 0, 0, 0, 0, 0};
517     m_openCLImageResult = clCreateImage(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
518         &clImageFormat, &imageDescriptor, source, &errorCode);
519 #else
520     m_openCLImageResult = clCreateImage2D(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
521         &clImageFormat, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, source, &errorCode);
522 #endif
523     if (context->isFailed(errorCode))
524         return 0;
525
526     return m_openCLImageResult;
527 }
528 #endif
529
530 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
531 {
532 #if USE(CG)
533     // CG handles color space adjustments internally.
534     UNUSED_PARAM(dstColorSpace);
535 #else
536     if (!hasResult() || dstColorSpace == m_resultColorSpace)
537         return;
538
539 #if ENABLE(OPENCL)
540     if (openCLImage()) {
541         if (m_imageBufferResult)
542             m_imageBufferResult = nullptr;
543         FilterContextOpenCL* context = FilterContextOpenCL::context();
544         ASSERT(context);
545         context->openCLTransformColorSpace(m_openCLImageResult, absolutePaintRect(), m_resultColorSpace, dstColorSpace);
546     } else {
547 #endif
548
549         // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
550         // color space transform support for the {pre,un}multiplied arrays.
551         asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
552
553 #if ENABLE(OPENCL)
554     }
555 #endif
556
557     m_resultColorSpace = dstColorSpace;
558
559     if (m_unmultipliedImageResult)
560         m_unmultipliedImageResult = nullptr;
561     if (m_premultipliedImageResult)
562         m_premultipliedImageResult = nullptr;
563 #endif
564 }
565
566 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
567 {
568     // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
569     // possible at the moment, because we need more detailed informations from the target object.
570     return ts;
571 }
572
573 } // namespace WebCore