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