Use "= default" to denote default constructor or destructor
[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 "ImageBuffer.h"
29 #include <runtime/JSCInlines.h>
30 #include <runtime/TypedArrayInlines.h>
31 #include <runtime/Uint8ClampedArray.h>
32 #include <wtf/text/TextStream.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(ColorSpaceSRGB)
50 {
51 }
52
53 FilterEffect::~FilterEffect() = default;
54
55 void FilterEffect::determineAbsolutePaintRect()
56 {
57     m_absolutePaintRect = IntRect();
58     for (auto& effect : m_inputEffects)
59         m_absolutePaintRect.unite(effect->absolutePaintRect());
60     clipAbsolutePaintRect();
61 }
62
63 void FilterEffect::clipAbsolutePaintRect()
64 {
65     // Filters in SVG clip to primitive subregion, while CSS doesn't.
66     if (m_clipsToBounds)
67         m_absolutePaintRect.intersect(enclosingIntRect(m_maxEffectRect));
68     else
69         m_absolutePaintRect.unite(enclosingIntRect(m_maxEffectRect));
70 }
71
72 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
73 {
74     ASSERT(hasResult());
75     IntPoint location = m_absolutePaintRect.location();
76     location.moveBy(-effectRect.location());
77     return IntRect(location, m_absolutePaintRect.size());
78 }
79
80 FloatRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
81 {
82     ASSERT(hasResult());
83
84     FloatSize scale;
85     ImageBuffer::clampedSize(m_absolutePaintRect.size(), scale);
86
87     AffineTransform transform;
88     transform.scale(scale).translate(-m_absolutePaintRect.location());
89     return transform.mapRect(srcRect);
90 }
91
92 FilterEffect* FilterEffect::inputEffect(unsigned number) const
93 {
94     ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size());
95     return m_inputEffects.at(number).get();
96 }
97
98 static unsigned collectEffects(const FilterEffect*effect, HashSet<const FilterEffect*>& allEffects)
99 {
100     allEffects.add(effect);
101     unsigned size = effect->numberOfEffectInputs();
102     for (unsigned i = 0; i < size; ++i) {
103         FilterEffect* in = effect->inputEffect(i);
104         collectEffects(in, allEffects);
105     }
106     return allEffects.size();
107 }
108
109 unsigned FilterEffect::totalNumberOfEffectInputs() const
110 {
111     HashSet<const FilterEffect*> allEffects;
112     return collectEffects(this, allEffects);
113 }
114
115 void FilterEffect::apply()
116 {
117     if (hasResult())
118         return;
119     unsigned size = m_inputEffects.size();
120     for (unsigned i = 0; i < size; ++i) {
121         FilterEffect* in = m_inputEffects.at(i).get();
122         in->apply();
123         if (!in->hasResult())
124             return;
125
126         // Convert input results to the current effect's color space.
127         transformResultColorSpace(in, i);
128     }
129
130     determineAbsolutePaintRect();
131     setResultColorSpace(m_operatingColorSpace);
132
133     if (m_absolutePaintRect.isEmpty() || ImageBuffer::sizeNeedsClamping(m_absolutePaintRect.size()))
134         return;
135
136     if (requiresValidPreMultipliedPixels()) {
137         for (unsigned i = 0; i < size; ++i)
138             inputEffect(i)->correctFilterResultIfNeeded();
139     }
140     
141     // Add platform specific apply functions here and return earlier.
142     platformApplySoftware();
143 }
144
145 void FilterEffect::forceValidPreMultipliedPixels()
146 {
147     // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
148     if (!m_premultipliedImageResult)
149         return;
150
151     Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
152     unsigned char* pixelData = imageArray->data();
153     int pixelArrayLength = imageArray->length();
154
155     // We must have four bytes per pixel, and complete pixels
156     ASSERT(!(pixelArrayLength % 4));
157
158 #if HAVE(ARM_NEON_INTRINSICS)
159     if (pixelArrayLength >= 64) {
160         unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
161         do {
162             // Increments pixelData by 64.
163             uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
164             sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
165             sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
166             sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
167             vst4q_u8(pixelData, sixteenPixels);
168             pixelData += 64;
169         } while (pixelData < lastPixel);
170
171         pixelArrayLength &= 0x3f;
172         if (!pixelArrayLength)
173             return;
174     }
175 #endif
176
177     int numPixels = pixelArrayLength / 4;
178
179     // Iterate over each pixel, checking alpha and adjusting color components if necessary
180     while (--numPixels >= 0) {
181         // Alpha is the 4th byte in a pixel
182         unsigned char a = *(pixelData + 3);
183         // Clamp each component to alpha, and increment the pixel location
184         for (int i = 0; i < 3; ++i) {
185             if (*pixelData > a)
186                 *pixelData = a;
187             ++pixelData;
188         }
189         // Increment for alpha
190         ++pixelData;
191     }
192 }
193
194 void FilterEffect::clearResult()
195 {
196     if (m_imageBufferResult)
197         m_imageBufferResult.reset();
198
199     m_unmultipliedImageResult = nullptr;
200     m_premultipliedImageResult = nullptr;
201 }
202
203 void FilterEffect::clearResultsRecursive()
204 {
205     // Clear all results, regardless that the current effect has
206     // a result. Can be used if an effect is in an erroneous state.
207     if (hasResult())
208         clearResult();
209
210     unsigned size = m_inputEffects.size();
211     for (unsigned i = 0; i < size; ++i)
212         m_inputEffects.at(i).get()->clearResultsRecursive();
213 }
214
215 ImageBuffer* FilterEffect::asImageBuffer()
216 {
217     if (!hasResult())
218         return nullptr;
219     if (m_imageBufferResult)
220         return m_imageBufferResult.get();
221     m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter.renderingMode(), m_filter.filterScale(), m_resultColorSpace);
222     if (!m_imageBufferResult)
223         return nullptr;
224
225     IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
226     if (m_premultipliedImageResult)
227         m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
228     else
229         m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
230     return m_imageBufferResult.get();
231 }
232
233 RefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
234 {
235     IntSize scaledSize(rect.size());
236     ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
237     scaledSize.scale(m_filter.filterScale());
238     auto imageData = Uint8ClampedArray::createUninitialized((scaledSize.area() * 4).unsafeGet());
239     copyUnmultipliedImage(imageData.get(), rect);
240     return imageData;
241 }
242
243 RefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
244 {
245     IntSize scaledSize(rect.size());
246     ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
247     scaledSize.scale(m_filter.filterScale());
248     auto imageData = Uint8ClampedArray::createUninitialized((scaledSize.area() * 4).unsafeGet());
249     copyPremultipliedImage(imageData.get(), rect);
250     return imageData;
251 }
252
253 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
254 {
255     IntRect scaledRect(rect);
256     scaledRect.scale(m_filter.filterScale());
257     IntSize scaledPaintSize(m_absolutePaintRect.size());
258     scaledPaintSize.scale(m_filter.filterScale());
259
260     if (!source || !destination)
261         return;
262
263     // Initialize the destination to transparent black, if not entirely covered by the source.
264     if (scaledRect.x() < 0 || scaledRect.y() < 0 || scaledRect.maxX() > scaledPaintSize.width() || scaledRect.maxY() > scaledPaintSize.height())
265         memset(destination->data(), 0, destination->length());
266
267     // Early return if the rect does not intersect with the source.
268     if (scaledRect.maxX() <= 0 || scaledRect.maxY() <= 0 || scaledRect.x() >= scaledPaintSize.width() || scaledRect.y() >= scaledPaintSize.height())
269         return;
270
271     int xOrigin = scaledRect.x();
272     int xDest = 0;
273     if (xOrigin < 0) {
274         xDest = -xOrigin;
275         xOrigin = 0;
276     }
277     int xEnd = scaledRect.maxX();
278     if (xEnd > scaledPaintSize.width())
279         xEnd = scaledPaintSize.width();
280
281     int yOrigin = scaledRect.y();
282     int yDest = 0;
283     if (yOrigin < 0) {
284         yDest = -yOrigin;
285         yOrigin = 0;
286     }
287     int yEnd = scaledRect.maxY();
288     if (yEnd > scaledPaintSize.height())
289         yEnd = scaledPaintSize.height();
290
291     int size = (xEnd - xOrigin) * 4;
292     int destinationScanline = scaledRect.width() * 4;
293     int sourceScanline = scaledPaintSize.width() * 4;
294     unsigned char *destinationPixel = destination->data() + ((yDest * scaledRect.width()) + xDest) * 4;
295     unsigned char *sourcePixel = source->data() + ((yOrigin * scaledPaintSize.width()) + xOrigin) * 4;
296
297     while (yOrigin < yEnd) {
298         memcpy(destinationPixel, sourcePixel, size);
299         destinationPixel += destinationScanline;
300         sourcePixel += sourceScanline;
301         ++yOrigin;
302     }
303 }
304
305 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
306 {
307     ASSERT(hasResult());
308
309     if (!m_unmultipliedImageResult) {
310         // We prefer a conversion from the image buffer.
311         if (m_imageBufferResult)
312             m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
313         else {
314             IntSize inputSize(m_absolutePaintRect.size());
315             ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
316             inputSize.scale(m_filter.filterScale());
317             m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized((inputSize.area() * 4).unsafeGet());
318             if (!m_unmultipliedImageResult) {
319                 WTFLogAlways("FilterEffect::copyUnmultipliedImage Unable to create buffer. Requested size was %d x %d\n", inputSize.width(), inputSize.height());
320                 return;
321             }
322             unsigned char* sourceComponent = m_premultipliedImageResult->data();
323             unsigned char* destinationComponent = m_unmultipliedImageResult->data();
324             unsigned char* end = sourceComponent + (inputSize.area() * 4).unsafeGet();
325             while (sourceComponent < end) {
326                 int alpha = sourceComponent[3];
327                 if (alpha) {
328                     destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
329                     destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
330                     destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
331                 } else {
332                     destinationComponent[0] = 0;
333                     destinationComponent[1] = 0;
334                     destinationComponent[2] = 0;
335                 }
336                 destinationComponent[3] = alpha;
337                 sourceComponent += 4;
338                 destinationComponent += 4;
339             }
340         }
341     }
342     copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
343 }
344
345 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
346 {
347     ASSERT(hasResult());
348
349     if (!m_premultipliedImageResult) {
350         // We prefer a conversion from the image buffer.
351         if (m_imageBufferResult)
352             m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
353         else {
354             IntSize inputSize(m_absolutePaintRect.size());
355             ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
356             inputSize.scale(m_filter.filterScale());
357             m_premultipliedImageResult = Uint8ClampedArray::createUninitialized((inputSize.area() * 4).unsafeGet());
358             if (!m_premultipliedImageResult) {
359                 WTFLogAlways("FilterEffect::copyPremultipliedImage Unable to create buffer. Requested size was %d x %d\n", inputSize.width(), inputSize.height());
360                 return;
361             }
362             unsigned char* sourceComponent = m_unmultipliedImageResult->data();
363             unsigned char* destinationComponent = m_premultipliedImageResult->data();
364             unsigned char* end = sourceComponent + (inputSize.area() * 4).unsafeGet();
365             while (sourceComponent < end) {
366                 int alpha = sourceComponent[3];
367                 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
368                 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
369                 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
370                 destinationComponent[3] = alpha;
371                 sourceComponent += 4;
372                 destinationComponent += 4;
373             }
374         }
375     }
376     copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
377 }
378
379 ImageBuffer* FilterEffect::createImageBufferResult()
380 {
381     // Only one result type is allowed.
382     ASSERT(!hasResult());
383     if (m_absolutePaintRect.isEmpty())
384         return nullptr;
385
386     FloatSize clampedSize = ImageBuffer::clampedSize(m_absolutePaintRect.size());
387     m_imageBufferResult = ImageBuffer::create(clampedSize, m_filter.renderingMode(), m_filter.filterScale(), m_resultColorSpace);
388     if (!m_imageBufferResult)
389         return nullptr;
390
391     return m_imageBufferResult.get();
392 }
393
394 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
395 {
396     // Only one result type is allowed.
397     ASSERT(!hasResult());
398     if (m_absolutePaintRect.isEmpty())
399         return nullptr;
400
401     IntSize resultSize(m_absolutePaintRect.size());
402     ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize));
403     resultSize.scale(m_filter.filterScale());
404     m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized((resultSize.area() * 4).unsafeGet());
405     return m_unmultipliedImageResult.get();
406 }
407
408 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
409 {
410     // Only one result type is allowed.
411     ASSERT(!hasResult());
412     if (m_absolutePaintRect.isEmpty())
413         return nullptr;
414
415     IntSize resultSize(m_absolutePaintRect.size());
416     ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize));
417     resultSize.scale(m_filter.filterScale());
418     m_premultipliedImageResult = Uint8ClampedArray::createUninitialized((resultSize.area() * 4).unsafeGet());
419     return m_premultipliedImageResult.get();
420 }
421
422 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
423 {
424 #if USE(CG)
425     // CG handles color space adjustments internally.
426     UNUSED_PARAM(dstColorSpace);
427 #else
428     if (!hasResult() || dstColorSpace == m_resultColorSpace)
429         return;
430
431     // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
432     // color space transform support for the {pre,un}multiplied arrays.
433     asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
434
435     m_resultColorSpace = dstColorSpace;
436
437     if (m_unmultipliedImageResult)
438         m_unmultipliedImageResult = nullptr;
439     if (m_premultipliedImageResult)
440         m_premultipliedImageResult = nullptr;
441 #endif
442 }
443
444 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
445 {
446     // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
447     // possible at the moment, because we need more detailed informations from the target object.
448     return ts;
449 }
450
451 } // namespace WebCore