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 "Filter.h"
30#include "GraphicsContext.h"
31#include "RenderTreeAsText.h"
32#include "TextStream.h"
33
34#include <wtf/ByteArray.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
118template <int b1, int b2, int b3, int b4>
119inline void computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength,
120                                    float k1, float k2, float k3, float k4)
121{
122    float scaledK4;
123    float scaledK1;
124    if (b1)
125        scaledK1 = k1 / 255.f;
126    if (b4)
127        scaledK4 = k4 * 255.f;
128
129    while (--pixelArrayLength >= 0) {
130        unsigned char i1 = *source;
131        unsigned char i2 = *destination;
132        float result = 0;
133        if (b1)
134            result += scaledK1 * i1 * i2;
135        if (b2)
136            result += k2 * i1;
137        if (b3)
138            result += k3 * i2;
139        if (b4)
140            result += scaledK4;
141
142        if (result <= 0)
143            *destination = 0;
144        else if (result >= 255)
145            *destination = 255;
146        else
147            *destination = result;
148        ++source;
149        ++destination;
150    }
151}
152
153inline void arithmetic(ByteArray* srcPixelArrayA, ByteArray* srcPixelArrayB,
154                       float k1, float k2, float k3, float k4)
155{
156    int pixelArrayLength = srcPixelArrayA->length();
157    ASSERT(pixelArrayLength == static_cast<int>(srcPixelArrayB->length()));
158    unsigned char* source = srcPixelArrayA->data();
159    unsigned char* destination = srcPixelArrayB->data();
160
161    if (!k4) {
162        if (!k1) {
163            computeArithmeticPixels<0, 1, 1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
164            return;
165        }
166
167        computeArithmeticPixels<1, 1, 1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
168        return;
169    }
170
171    if (!k1) {
172        computeArithmeticPixels<0, 1, 1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
173        return;
174    }
175    computeArithmeticPixels<1, 1, 1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
176}
177
178void FEComposite::determineAbsolutePaintRect()
179{
180    switch (m_type) {
181    case FECOMPOSITE_OPERATOR_IN:
182    case FECOMPOSITE_OPERATOR_ATOP:
183        // For In and Atop the first effect just influences the result of
184        // the second effect. So just use the absolute paint rect of the second effect here.
185        setAbsolutePaintRect(inputEffect(1)->absolutePaintRect());
186        return;
187    case FECOMPOSITE_OPERATOR_ARITHMETIC:
188        // Arithmetic may influnce the compele filter primitive region. So we can't
189        // optimize the paint region here.
190        setAbsolutePaintRect(enclosingIntRect(maxEffectRect()));
191        return;
192    default:
193        // Take the union of both input effects.
194        FilterEffect::determineAbsolutePaintRect();
195        return;
196    }
197}
198
199void FEComposite::apply()
200{
201    if (hasResult())
202        return;
203    FilterEffect* in = inputEffect(0);
204    FilterEffect* in2 = inputEffect(1);
205    in->apply();
206    in2->apply();
207    if (!in->hasResult() || !in2->hasResult())
208        return;
209
210    if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) {
211        ByteArray* dstPixelArray = createPremultipliedImageResult();
212        if (!dstPixelArray)
213            return;
214
215        IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
216        RefPtr<ByteArray> srcPixelArray = in->asPremultipliedImage(effectADrawingRect);
217
218        IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect());
219        in2->copyPremultipliedImage(dstPixelArray, effectBDrawingRect);
220
221        arithmetic(srcPixelArray.get(), dstPixelArray, m_k1, m_k2, m_k3, m_k4);
222        return;
223    }
224
225    ImageBuffer* resultImage = createImageBufferResult();
226    if (!resultImage)
227        return;
228    GraphicsContext* filterContext = resultImage->context();
229
230    FloatRect srcRect = FloatRect(0, 0, -1, -1);
231    switch (m_type) {
232    case FECOMPOSITE_OPERATOR_OVER:
233        filterContext->drawImageBuffer(in2->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()));
234        filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));
235        break;
236    case FECOMPOSITE_OPERATOR_IN:
237        filterContext->save();
238        filterContext->clipToImageBuffer(in2->asImageBuffer(), drawingRegionOfInputImage(in2->absolutePaintRect()));
239        filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));
240        filterContext->restore();
241        break;
242    case FECOMPOSITE_OPERATOR_OUT:
243        filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()));
244        filterContext->drawImageBuffer(in2->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()), srcRect, CompositeDestinationOut);
245        break;
246    case FECOMPOSITE_OPERATOR_ATOP:
247        filterContext->drawImageBuffer(in2->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()));
248        filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), srcRect, CompositeSourceAtop);
249        break;
250    case FECOMPOSITE_OPERATOR_XOR:
251        filterContext->drawImageBuffer(in2->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()));
252        filterContext->drawImageBuffer(in->asImageBuffer(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), srcRect, CompositeXOR);
253        break;
254    default:
255        break;
256    }
257}
258
259void FEComposite::dump()
260{
261}
262
263static TextStream& operator<<(TextStream& ts, const CompositeOperationType& type)
264{
265    switch (type) {
266    case FECOMPOSITE_OPERATOR_UNKNOWN:
267        ts << "UNKNOWN";
268        break;
269    case FECOMPOSITE_OPERATOR_OVER:
270        ts << "OVER";
271        break;
272    case FECOMPOSITE_OPERATOR_IN:
273        ts << "IN";
274        break;
275    case FECOMPOSITE_OPERATOR_OUT:
276        ts << "OUT";
277        break;
278    case FECOMPOSITE_OPERATOR_ATOP:
279        ts << "ATOP";
280        break;
281    case FECOMPOSITE_OPERATOR_XOR:
282        ts << "XOR";
283        break;
284    case FECOMPOSITE_OPERATOR_ARITHMETIC:
285        ts << "ARITHMETIC";
286        break;
287    }
288    return ts;
289}
290
291TextStream& FEComposite::externalRepresentation(TextStream& ts, int indent) const
292{
293    writeIndent(ts, indent);
294    ts << "[feComposite";
295    FilterEffect::externalRepresentation(ts);
296    ts << " operation=\"" << m_type << "\"";
297    if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC)
298        ts << " k1=\"" << m_k1 << "\" k2=\"" << m_k2 << "\" k3=\"" << m_k3 << "\" k4=\"" << m_k4 << "\"";
299    ts << "]\n";
300    inputEffect(0)->externalRepresentation(ts, indent + 1);
301    inputEffect(1)->externalRepresentation(ts, indent + 1);
302    return ts;
303}
304
305} // namespace WebCore
306
307#endif // ENABLE(FILTERS)
308