ec9181af38c1ce1ba2eaf28f7e55f25535ed9ee6
[WebKit-https.git] / Source / WebCore / platform / graphics / filters / FEComposite.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
4  * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
6  * Copyright (C) Research In Motion Limited 2010. 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 "FEComposite.h"
26
27 #include "FECompositeArithmeticNEON.h"
28 #include "Filter.h"
29 #include "GraphicsContext.h"
30 #include <wtf/text/TextStream.h>
31
32 #include <runtime/Uint8ClampedArray.h>
33
34 namespace WebCore {
35
36 FEComposite::FEComposite(Filter& filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
37     : FilterEffect(filter)
38     , m_type(type)
39     , m_k1(k1)
40     , m_k2(k2)
41     , m_k3(k3)
42     , m_k4(k4)
43 {
44 }
45
46 Ref<FEComposite> FEComposite::create(Filter& filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
47 {
48     return adoptRef(*new FEComposite(filter, type, k1, k2, k3, k4));
49 }
50
51 bool FEComposite::setOperation(CompositeOperationType type)
52 {
53     if (m_type == type)
54         return false;
55     m_type = type;
56     return true;
57 }
58
59 bool FEComposite::setK1(float k1)
60 {
61     if (m_k1 == k1)
62         return false;
63     m_k1 = k1;
64     return true;
65 }
66
67 bool FEComposite::setK2(float k2)
68 {
69     if (m_k2 == k2)
70         return false;
71     m_k2 = k2;
72     return true;
73 }
74
75 bool FEComposite::setK3(float k3)
76 {
77     if (m_k3 == k3)
78         return false;
79     m_k3 = k3;
80     return true;
81 }
82
83 bool FEComposite::setK4(float k4)
84 {
85     if (m_k4 == k4)
86         return false;
87     m_k4 = k4;
88     return true;
89 }
90
91 void FEComposite::correctFilterResultIfNeeded()
92 {
93     if (m_type != FECOMPOSITE_OPERATOR_ARITHMETIC)
94         return;
95
96     forceValidPreMultipliedPixels();
97 }
98     
99 static unsigned char clampByte(int c)
100 {
101     unsigned char buff[] = { static_cast<unsigned char>(c), 255, 0 };
102     unsigned uc = static_cast<unsigned>(c);
103     return buff[!!(uc & ~0xff) + !!(uc & ~(~0u >> 1))];
104 }
105
106 template <int b1, int b4>
107 static inline void computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength,
108                                     float k1, float k2, float k3, float k4)
109 {
110     float scaledK1;
111     float scaledK4;
112     if (b1)
113         scaledK1 = k1 / 255.0f;
114     if (b4)
115         scaledK4 = k4 * 255.0f;
116
117     while (--pixelArrayLength >= 0) {
118         unsigned char i1 = *source;
119         unsigned char i2 = *destination;
120         float result = k2 * i1 + k3 * i2;
121         if (b1)
122             result += scaledK1 * i1 * i2;
123         if (b4)
124             result += scaledK4;
125
126         *destination = clampByte(result);
127         ++source;
128         ++destination;
129     }
130 }
131
132 // computeArithmeticPixelsUnclamped is a faster version of computeArithmeticPixels for the common case where clamping
133 // is not necessary. This enables aggresive compiler optimizations such as auto-vectorization.
134 template <int b1, int b4>
135 static inline void computeArithmeticPixelsUnclamped(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4)
136 {
137     float scaledK1;
138     float scaledK4;
139     if (b1)
140         scaledK1 = k1 / 255.0f;
141     if (b4)
142         scaledK4 = k4 * 255.0f;
143
144     while (--pixelArrayLength >= 0) {
145         unsigned char i1 = *source;
146         unsigned char i2 = *destination;
147         float result = k2 * i1 + k3 * i2;
148         if (b1)
149             result += scaledK1 * i1 * i2;
150         if (b4)
151             result += scaledK4;
152
153         *destination = result;
154         ++source;
155         ++destination;
156     }
157 }
158
159 #if !HAVE(ARM_NEON_INTRINSICS)
160 static inline void arithmeticSoftware(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4)
161 {
162     float upperLimit = std::max(0.0f, k1) + std::max(0.0f, k2) + std::max(0.0f, k3) + k4;
163     float lowerLimit = std::min(0.0f, k1) + std::min(0.0f, k2) + std::min(0.0f, k3) + k4;
164     if ((k4 >= 0.0f && k4 <= 1.0f) && (upperLimit >= 0.0f && upperLimit <= 1.0f) && (lowerLimit >= 0.0f && lowerLimit <= 1.0f)) {
165         if (k4) {
166             if (k1)
167                 computeArithmeticPixelsUnclamped<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
168             else
169                 computeArithmeticPixelsUnclamped<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
170         } else {
171             if (k1)
172                 computeArithmeticPixelsUnclamped<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
173             else
174                 computeArithmeticPixelsUnclamped<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
175         }
176         return;
177     }
178
179     if (k4) {
180         if (k1)
181             computeArithmeticPixels<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
182         else
183             computeArithmeticPixels<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
184     } else {
185         if (k1)
186             computeArithmeticPixels<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
187         else
188             computeArithmeticPixels<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
189     }
190 }
191 #endif
192
193 inline void FEComposite::platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination,
194     float k1, float k2, float k3, float k4)
195 {
196     int length = source->length();
197     ASSERT(length == static_cast<int>(destination->length()));
198     // The selection here eventually should happen dynamically.
199 #if HAVE(ARM_NEON_INTRINSICS)
200     ASSERT(!(length & 0x3));
201     platformArithmeticNeon(source->data(), destination->data(), length, k1, k2, k3, k4);
202 #else
203     arithmeticSoftware(source->data(), destination->data(), length, k1, k2, k3, k4);
204 #endif
205 }
206
207 void FEComposite::determineAbsolutePaintRect()
208 {
209     switch (m_type) {
210     case FECOMPOSITE_OPERATOR_IN:
211     case FECOMPOSITE_OPERATOR_ATOP:
212         // For In and Atop the first effect just influences the result of
213         // the second effect. So just use the absolute paint rect of the second effect here.
214         setAbsolutePaintRect(inputEffect(1)->absolutePaintRect());
215         clipAbsolutePaintRect();
216         return;
217     case FECOMPOSITE_OPERATOR_ARITHMETIC:
218         // Arithmetic may influnce the compele filter primitive region. So we can't
219         // optimize the paint region here.
220         setAbsolutePaintRect(enclosingIntRect(maxEffectRect()));
221         return;
222     default:
223         // Take the union of both input effects.
224         FilterEffect::determineAbsolutePaintRect();
225         return;
226     }
227 }
228
229 void FEComposite::platformApplySoftware()
230 {
231     FilterEffect* in = inputEffect(0);
232     FilterEffect* in2 = inputEffect(1);
233
234     if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) {
235         Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult();
236         if (!dstPixelArray)
237             return;
238
239         IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
240         RefPtr<Uint8ClampedArray> srcPixelArray = in->asPremultipliedImage(effectADrawingRect);
241
242         IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect());
243         in2->copyPremultipliedImage(dstPixelArray, effectBDrawingRect);
244
245         platformArithmeticSoftware(srcPixelArray.get(), dstPixelArray, m_k1, m_k2, m_k3, m_k4);
246         return;
247     }
248
249     ImageBuffer* resultImage = createImageBufferResult();
250     if (!resultImage)
251         return;
252     GraphicsContext& filterContext = resultImage->context();
253
254     ImageBuffer* imageBuffer = in->asImageBuffer();
255     ImageBuffer* imageBuffer2 = in2->asImageBuffer();
256     if (!imageBuffer || !imageBuffer2)
257         return;
258
259     switch (m_type) {
260     case FECOMPOSITE_OPERATOR_OVER:
261         filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
262         filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
263         break;
264     case FECOMPOSITE_OPERATOR_IN: {
265         // Applies only to the intersected region.
266         IntRect destinationRect = in->absolutePaintRect();
267         destinationRect.intersect(in2->absolutePaintRect());
268         destinationRect.intersect(absolutePaintRect());
269         if (destinationRect.isEmpty())
270             break;
271         IntRect adjustedDestinationRect = destinationRect - absolutePaintRect().location();
272         IntRect sourceRect = destinationRect - in->absolutePaintRect().location();
273         IntRect source2Rect = destinationRect - in2->absolutePaintRect().location();
274         filterContext.drawImageBuffer(*imageBuffer2, adjustedDestinationRect, source2Rect);
275         filterContext.drawImageBuffer(*imageBuffer, adjustedDestinationRect, sourceRect, CompositeSourceIn);
276         break;
277     }
278     case FECOMPOSITE_OPERATOR_OUT:
279         filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
280         filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()), IntRect(IntPoint(), imageBuffer2->logicalSize()), CompositeDestinationOut);
281         break;
282     case FECOMPOSITE_OPERATOR_ATOP:
283         filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
284         filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeSourceAtop);
285         break;
286     case FECOMPOSITE_OPERATOR_XOR:
287         filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
288         filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeXOR);
289         break;
290     case FECOMPOSITE_OPERATOR_LIGHTER:
291         filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
292         filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositePlusLighter);
293         break;
294     default:
295         break;
296     }
297 }
298
299 static TextStream& operator<<(TextStream& ts, const CompositeOperationType& type)
300 {
301     switch (type) {
302     case FECOMPOSITE_OPERATOR_UNKNOWN:
303         ts << "UNKNOWN";
304         break;
305     case FECOMPOSITE_OPERATOR_OVER:
306         ts << "OVER";
307         break;
308     case FECOMPOSITE_OPERATOR_IN:
309         ts << "IN";
310         break;
311     case FECOMPOSITE_OPERATOR_OUT:
312         ts << "OUT";
313         break;
314     case FECOMPOSITE_OPERATOR_ATOP:
315         ts << "ATOP";
316         break;
317     case FECOMPOSITE_OPERATOR_XOR:
318         ts << "XOR";
319         break;
320     case FECOMPOSITE_OPERATOR_ARITHMETIC:
321         ts << "ARITHMETIC";
322         break;
323     case FECOMPOSITE_OPERATOR_LIGHTER:
324         ts << "LIGHTER";
325         break;
326     }
327     return ts;
328 }
329
330 TextStream& FEComposite::externalRepresentation(TextStream& ts, int indent) const
331 {
332     writeIndent(ts, indent);
333     ts << "[feComposite";
334     FilterEffect::externalRepresentation(ts);
335     ts << " operation=\"" << m_type << "\"";
336     if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC)
337         ts << " k1=\"" << m_k1 << "\" k2=\"" << m_k2 << "\" k3=\"" << m_k3 << "\" k4=\"" << m_k4 << "\"";
338     ts << "]\n";
339     inputEffect(0)->externalRepresentation(ts, indent + 1);
340     inputEffect(1)->externalRepresentation(ts, indent + 1);
341     return ts;
342 }
343
344 } // namespace WebCore