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