feMorphology stops applying if either x or y radius is 0 but should not.
[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-2016 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 "GeometryUtilities.h"
29 #include "ImageBuffer.h"
30 #include "Logging.h"
31 #include <runtime/JSCInlines.h>
32 #include <runtime/TypedArrayInlines.h>
33 #include <runtime/Uint8ClampedArray.h>
34 #include <wtf/text/TextStream.h>
35
36 #if HAVE(ARM_NEON_INTRINSICS)
37 #include <arm_neon.h>
38 #endif
39
40 #if USE(ACCELERATE)
41 #include <Accelerate/Accelerate.h>
42 #endif
43
44 namespace WebCore {
45
46 FilterEffect::FilterEffect(Filter& filter)
47     : m_filter(filter)
48 {
49 }
50
51 FilterEffect::~FilterEffect() = default;
52
53 void FilterEffect::determineAbsolutePaintRect()
54 {
55     m_absolutePaintRect = IntRect();
56     for (auto& effect : m_inputEffects)
57         m_absolutePaintRect.unite(effect->absolutePaintRect());
58     clipAbsolutePaintRect();
59 }
60
61 void FilterEffect::clipAbsolutePaintRect()
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 FloatPoint FilterEffect::mapPointFromUserSpaceToBuffer(FloatPoint userSpacePoint) const
71 {
72     FloatPoint absolutePoint = mapPoint(userSpacePoint, m_filterPrimitiveSubregion, m_absoluteUnclippedSubregion);
73     absolutePoint.moveBy(-m_absolutePaintRect.location());
74     return absolutePoint;
75 }
76
77 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
78 {
79     ASSERT(hasResult());
80     IntPoint location = m_absolutePaintRect.location();
81     location.moveBy(-effectRect.location());
82     return IntRect(location, m_absolutePaintRect.size());
83 }
84
85 FloatRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
86 {
87     ASSERT(hasResult());
88
89     FloatSize scale;
90     ImageBuffer::clampedSize(m_absolutePaintRect.size(), scale);
91
92     AffineTransform transform;
93     transform.scale(scale).translate(-m_absolutePaintRect.location());
94     return transform.mapRect(srcRect);
95 }
96
97 FilterEffect* FilterEffect::inputEffect(unsigned number) const
98 {
99     ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size());
100     return m_inputEffects.at(number).get();
101 }
102
103 static unsigned collectEffects(const FilterEffect*effect, HashSet<const FilterEffect*>& allEffects)
104 {
105     allEffects.add(effect);
106     unsigned size = effect->numberOfEffectInputs();
107     for (unsigned i = 0; i < size; ++i) {
108         FilterEffect* in = effect->inputEffect(i);
109         collectEffects(in, allEffects);
110     }
111     return allEffects.size();
112 }
113
114 unsigned FilterEffect::totalNumberOfEffectInputs() const
115 {
116     HashSet<const FilterEffect*> allEffects;
117     return collectEffects(this, allEffects);
118 }
119
120 void FilterEffect::apply()
121 {
122     if (hasResult())
123         return;
124     unsigned size = m_inputEffects.size();
125     for (unsigned i = 0; i < size; ++i) {
126         FilterEffect* in = m_inputEffects.at(i).get();
127         in->apply();
128         if (!in->hasResult())
129             return;
130
131         // Convert input results to the current effect's color space.
132         transformResultColorSpace(in, i);
133     }
134
135     determineAbsolutePaintRect();
136     setResultColorSpace(m_operatingColorSpace);
137
138     if (m_absolutePaintRect.isEmpty() || ImageBuffer::sizeNeedsClamping(m_absolutePaintRect.size()))
139         return;
140
141     if (requiresValidPreMultipliedPixels()) {
142         for (unsigned i = 0; i < size; ++i)
143             inputEffect(i)->correctFilterResultIfNeeded();
144     }
145     
146     // Add platform specific apply functions here and return earlier.
147     platformApplySoftware();
148 }
149
150 void FilterEffect::forceValidPreMultipliedPixels()
151 {
152     // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
153     if (!m_premultipliedImageResult)
154         return;
155
156     Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
157     uint8_t* pixelData = imageArray->data();
158     int pixelArrayLength = imageArray->length();
159
160     // We must have four bytes per pixel, and complete pixels
161     ASSERT(!(pixelArrayLength % 4));
162
163 #if HAVE(ARM_NEON_INTRINSICS)
164     if (pixelArrayLength >= 64) {
165         uint8_t* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
166         do {
167             // Increments pixelData by 64.
168             uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
169             sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
170             sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
171             sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
172             vst4q_u8(pixelData, sixteenPixels);
173             pixelData += 64;
174         } while (pixelData < lastPixel);
175
176         pixelArrayLength &= 0x3f;
177         if (!pixelArrayLength)
178             return;
179     }
180 #endif
181
182     int numPixels = pixelArrayLength / 4;
183
184     // Iterate over each pixel, checking alpha and adjusting color components if necessary
185     while (--numPixels >= 0) {
186         // Alpha is the 4th byte in a pixel
187         uint8_t a = *(pixelData + 3);
188         // Clamp each component to alpha, and increment the pixel location
189         for (int i = 0; i < 3; ++i) {
190             if (*pixelData > a)
191                 *pixelData = a;
192             ++pixelData;
193         }
194         // Increment for alpha
195         ++pixelData;
196     }
197 }
198
199 void FilterEffect::clearResult()
200 {
201     if (m_imageBufferResult)
202         m_imageBufferResult.reset();
203
204     m_unmultipliedImageResult = nullptr;
205     m_premultipliedImageResult = nullptr;
206 }
207
208 void FilterEffect::clearResultsRecursive()
209 {
210     // Clear all results, regardless that the current effect has
211     // a result. Can be used if an effect is in an erroneous state.
212     if (hasResult())
213         clearResult();
214
215     unsigned size = m_inputEffects.size();
216     for (unsigned i = 0; i < size; ++i)
217         m_inputEffects.at(i).get()->clearResultsRecursive();
218 }
219
220 ImageBuffer* FilterEffect::imageBufferResult()
221 {
222     LOG_WITH_STREAM(Filters, stream << "FilterEffect " << filterName() << " " << this << " imageBufferResult(). Existing image buffer " << m_imageBufferResult.get() <<  " m_premultipliedImageResult " << m_premultipliedImageResult.get() << " m_unmultipliedImageResult " << m_unmultipliedImageResult.get());
223
224     if (!hasResult())
225         return nullptr;
226
227     if (m_imageBufferResult)
228         return m_imageBufferResult.get();
229
230     m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter.renderingMode(), m_filter.filterScale(), m_resultColorSpace);
231     if (!m_imageBufferResult)
232         return nullptr;
233
234     IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
235     if (m_premultipliedImageResult)
236         m_imageBufferResult->putByteArray(*m_premultipliedImageResult, AlphaPremultiplication::Premultiplied, destinationRect.size(), destinationRect, IntPoint());
237     else
238         m_imageBufferResult->putByteArray(*m_unmultipliedImageResult, AlphaPremultiplication::Unpremultiplied, destinationRect.size(), destinationRect, IntPoint());
239     return m_imageBufferResult.get();
240 }
241
242 RefPtr<Uint8ClampedArray> FilterEffect::unmultipliedResult(const IntRect& rect)
243 {
244     IntSize scaledSize(rect.size());
245     ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
246     scaledSize.scale(m_filter.filterScale());
247     auto imageData = Uint8ClampedArray::createUninitialized((scaledSize.area() * 4).unsafeGet());
248     if (!imageData)
249         return nullptr;
250
251     copyUnmultipliedResult(*imageData, rect);
252     return imageData;
253 }
254
255 RefPtr<Uint8ClampedArray> FilterEffect::premultipliedResult(const IntRect& rect)
256 {
257     IntSize scaledSize(rect.size());
258     ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
259     scaledSize.scale(m_filter.filterScale());
260     auto imageData = Uint8ClampedArray::createUninitialized((scaledSize.area() * 4).unsafeGet());
261     if (!imageData)
262         return nullptr;
263     copyPremultipliedResult(*imageData, rect);
264     return imageData;
265 }
266
267 void FilterEffect::copyImageBytes(const Uint8ClampedArray& source, Uint8ClampedArray& destination, const IntRect& rect) const
268 {
269     IntRect scaledRect(rect);
270     scaledRect.scale(m_filter.filterScale());
271     IntSize scaledPaintSize(m_absolutePaintRect.size());
272     scaledPaintSize.scale(m_filter.filterScale());
273
274     // Initialize the destination to transparent black, if not entirely covered by the source.
275     if (scaledRect.x() < 0 || scaledRect.y() < 0 || scaledRect.maxX() > scaledPaintSize.width() || scaledRect.maxY() > scaledPaintSize.height())
276         memset(destination.data(), 0, destination.length());
277
278     // Early return if the rect does not intersect with the source.
279     if (scaledRect.maxX() <= 0 || scaledRect.maxY() <= 0 || scaledRect.x() >= scaledPaintSize.width() || scaledRect.y() >= scaledPaintSize.height())
280         return;
281
282     int xOrigin = scaledRect.x();
283     int xDest = 0;
284     if (xOrigin < 0) {
285         xDest = -xOrigin;
286         xOrigin = 0;
287     }
288     int xEnd = scaledRect.maxX();
289     if (xEnd > scaledPaintSize.width())
290         xEnd = scaledPaintSize.width();
291
292     int yOrigin = scaledRect.y();
293     int yDest = 0;
294     if (yOrigin < 0) {
295         yDest = -yOrigin;
296         yOrigin = 0;
297     }
298     int yEnd = scaledRect.maxY();
299     if (yEnd > scaledPaintSize.height())
300         yEnd = scaledPaintSize.height();
301
302     int size = (xEnd - xOrigin) * 4;
303     int destinationScanline = scaledRect.width() * 4;
304     int sourceScanline = scaledPaintSize.width() * 4;
305     uint8_t* destinationPixel = destination.data() + ((yDest * scaledRect.width()) + xDest) * 4;
306     const uint8_t* sourcePixel = source.data() + ((yOrigin * scaledPaintSize.width()) + xOrigin) * 4;
307
308     while (yOrigin < yEnd) {
309         memcpy(destinationPixel, sourcePixel, size);
310         destinationPixel += destinationScanline;
311         sourcePixel += sourceScanline;
312         ++yOrigin;
313     }
314 }
315
316 static void copyPremultiplyingAlpha(const Uint8ClampedArray& source, Uint8ClampedArray& destination, const IntSize& inputSize)
317 {
318 #if USE(ACCELERATE)
319     size_t rowBytes = inputSize.width() * 4;
320
321     vImage_Buffer src;
322     src.width = inputSize.width();
323     src.height = inputSize.height();
324     src.rowBytes = rowBytes;
325     src.data = reinterpret_cast<void*>(source.data());
326
327     vImage_Buffer dest;
328     dest.width = inputSize.width();
329     dest.height = inputSize.height();
330     dest.rowBytes = rowBytes;
331     dest.data = reinterpret_cast<void*>(destination.data());
332
333     vImagePremultiplyData_RGBA8888(&src, &dest, kvImageNoFlags);
334 #else
335     const uint8_t* sourceComponent = source.data();
336     const uint8_t* end = sourceComponent + (inputSize.area() * 4).unsafeGet();
337     uint8_t* destinationComponent = destination.data();
338
339     while (sourceComponent < end) {
340         int alpha = sourceComponent[3];
341         destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
342         destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
343         destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
344         destinationComponent[3] = alpha;
345         sourceComponent += 4;
346         destinationComponent += 4;
347     }
348 #endif
349 }
350
351 static void copyUnpremultiplyingAlpha(const Uint8ClampedArray& source, Uint8ClampedArray& destination, const IntSize& inputSize)
352 {
353 #if USE(ACCELERATE)
354     size_t rowBytes = inputSize.width() * 4;
355
356     vImage_Buffer src;
357     src.width = inputSize.width();
358     src.height = inputSize.height();
359     src.rowBytes = rowBytes;
360     src.data = reinterpret_cast<void*>(source.data());
361
362     vImage_Buffer dest;
363     dest.width = inputSize.width();
364     dest.height = inputSize.height();
365     dest.rowBytes = rowBytes;
366     dest.data = reinterpret_cast<void*>(destination.data());
367
368     vImageUnpremultiplyData_RGBA8888(&src, &dest, kvImageNoFlags);
369 #else
370     const uint8_t* sourceComponent = source.data();
371     const uint8_t* end = sourceComponent + (inputSize.area() * 4).unsafeGet();
372     uint8_t* destinationComponent = destination.data();
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 #endif
389 }
390
391 void FilterEffect::copyUnmultipliedResult(Uint8ClampedArray& destination, const IntRect& rect)
392 {
393     ASSERT(hasResult());
394     
395     LOG_WITH_STREAM(Filters, stream << "FilterEffect " << filterName() << " " << this << " copyUnmultipliedResult(). Existing image buffer " << m_imageBufferResult.get() <<  " m_premultipliedImageResult " << m_premultipliedImageResult.get() << " m_unmultipliedImageResult " << m_unmultipliedImageResult.get());
396
397     if (!m_unmultipliedImageResult) {
398         // We prefer a conversion from the image buffer.
399         if (m_imageBufferResult) {
400             m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
401             if (!m_unmultipliedImageResult)
402                 return;
403         } else {
404             IntSize inputSize(m_absolutePaintRect.size());
405             ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
406             inputSize.scale(m_filter.filterScale());
407             m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized((inputSize.area() * 4).unsafeGet());
408             if (!m_unmultipliedImageResult)
409                 return;
410             
411             copyUnpremultiplyingAlpha(*m_premultipliedImageResult, *m_unmultipliedImageResult, inputSize);
412         }
413     }
414     copyImageBytes(*m_unmultipliedImageResult, destination, rect);
415 }
416
417 void FilterEffect::copyPremultipliedResult(Uint8ClampedArray& destination, const IntRect& rect)
418 {
419     ASSERT(hasResult());
420
421     LOG_WITH_STREAM(Filters, stream << "FilterEffect " << filterName() << " " << this << " copyPremultipliedResult(). Existing image buffer " << m_imageBufferResult.get() <<  " m_premultipliedImageResult " << m_premultipliedImageResult.get() << " m_unmultipliedImageResult " << m_unmultipliedImageResult.get());
422
423     if (!m_premultipliedImageResult) {
424         // We prefer a conversion from the image buffer.
425         if (m_imageBufferResult) {
426             m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
427             if (!m_premultipliedImageResult)
428                 return;
429         } else {
430             IntSize inputSize(m_absolutePaintRect.size());
431             ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
432             inputSize.scale(m_filter.filterScale());
433             m_premultipliedImageResult = Uint8ClampedArray::createUninitialized((inputSize.area() * 4).unsafeGet());
434             if (!m_premultipliedImageResult)
435                 return;
436             
437             copyPremultiplyingAlpha(*m_unmultipliedImageResult, *m_premultipliedImageResult, inputSize);
438         }
439     }
440     copyImageBytes(*m_premultipliedImageResult, destination, rect);
441 }
442
443 ImageBuffer* FilterEffect::createImageBufferResult()
444 {
445     LOG(Filters, "FilterEffect %s %p createImageBufferResult %dx%d", filterName(), this, m_absolutePaintRect.size().width(), m_absolutePaintRect.size().height());
446
447     // Only one result type is allowed.
448     ASSERT(!hasResult());
449     if (m_absolutePaintRect.isEmpty())
450         return nullptr;
451
452     FloatSize clampedSize = ImageBuffer::clampedSize(m_absolutePaintRect.size());
453     m_imageBufferResult = ImageBuffer::create(clampedSize, m_filter.renderingMode(), m_filter.filterScale(), m_resultColorSpace);
454     if (!m_imageBufferResult)
455         return nullptr;
456
457     return m_imageBufferResult.get();
458 }
459
460 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
461 {
462     LOG(Filters, "FilterEffect %s %p createUnmultipliedImageResult", filterName(), this);
463
464     // Only one result type is allowed.
465     ASSERT(!hasResult());
466     if (m_absolutePaintRect.isEmpty())
467         return nullptr;
468
469     IntSize resultSize(m_absolutePaintRect.size());
470     ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize));
471     resultSize.scale(m_filter.filterScale());
472     m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized((resultSize.area() * 4).unsafeGet());
473     return m_unmultipliedImageResult.get();
474 }
475
476 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
477 {
478     LOG(Filters, "FilterEffect %s %p createPremultipliedImageResult", filterName(), this);
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::sizeNeedsClamping(resultSize));
487     resultSize.scale(m_filter.filterScale());
488     m_premultipliedImageResult = Uint8ClampedArray::createUninitialized((resultSize.area() * 4).unsafeGet());
489     return m_premultipliedImageResult.get();
490 }
491
492 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
493 {
494 #if USE(CG)
495     // CG handles color space adjustments internally.
496     UNUSED_PARAM(dstColorSpace);
497 #else
498     if (!hasResult() || dstColorSpace == m_resultColorSpace)
499         return;
500
501     // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
502     // color space transform support for the {pre,un}multiplied arrays.
503     imageBufferResult()->transformColorSpace(m_resultColorSpace, dstColorSpace);
504
505     m_resultColorSpace = dstColorSpace;
506
507     if (m_unmultipliedImageResult)
508         m_unmultipliedImageResult = nullptr;
509     if (m_premultipliedImageResult)
510         m_premultipliedImageResult = nullptr;
511 #endif
512 }
513
514 TextStream& FilterEffect::externalRepresentation(TextStream& ts, RepresentationType representationType) const
515 {
516     // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
517     // possible at the moment, because we need more detailed informations from the target object.
518     
519     if (representationType == RepresentationType::Debugging) {
520         TextStream::IndentScope indentScope(ts);
521         ts.dumpProperty("alpha image", m_alphaImage);
522         ts.dumpProperty("operating colorspace", m_operatingColorSpace);
523         ts.dumpProperty("result colorspace", m_resultColorSpace);
524         ts << "\n" << indent;
525     }
526     return ts;
527 }
528
529 TextStream& operator<<(TextStream& ts, const FilterEffect& filter)
530 {
531     // Use a new stream because we want multiline mode for logging filters.
532     TextStream filterStream;
533     filter.externalRepresentation(filterStream, FilterEffect::RepresentationType::Debugging);
534     
535     return ts << filterStream.release();
536 }
537
538 } // namespace WebCore