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