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 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23
24#if ENABLE(FILTERS)
25#include "FilterEffect.h"
26
27#include "Filter.h"
28#include "ImageBuffer.h"
29#include "TextStream.h"
30#include <wtf/ByteArray.h>
31
32namespace WebCore {
33
34FilterEffect::FilterEffect(Filter* filter)
35    : m_alphaImage(false)
36    , m_filter(filter)
37    , m_hasX(false)
38    , m_hasY(false)
39    , m_hasWidth(false)
40    , m_hasHeight(false)
41{
42    ASSERT(m_filter);
43}
44
45FilterEffect::~FilterEffect()
46{
47}
48
49inline bool isFilterSizeValid(IntRect rect)
50{
51    if (rect.width() < 0 || rect.width() > kMaxFilterSize
52        || rect.height() < 0 || rect.height() > kMaxFilterSize)
53        return false;
54    return true;
55}
56
57void FilterEffect::determineAbsolutePaintRect()
58{
59    m_absolutePaintRect = IntRect();
60    unsigned size = m_inputEffects.size();
61    for (unsigned i = 0; i < size; ++i)
62        m_absolutePaintRect.unite(m_inputEffects.at(i)->absolutePaintRect());
63
64    // SVG specification wants us to clip to primitive subregion.
65    m_absolutePaintRect.intersect(enclosingIntRect(m_maxEffectRect));
66}
67
68IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
69{
70    ASSERT(hasResult());
71    IntPoint location = m_absolutePaintRect.location();
72    location.move(-effectRect.x(), -effectRect.y());
73    return IntRect(location, m_absolutePaintRect.size());
74}
75
76IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
77{
78    return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(),
79                            srcRect.y() - m_absolutePaintRect.y()), srcRect.size());
80}
81
82FilterEffect* FilterEffect::inputEffect(unsigned number) const
83{
84    ASSERT(number < m_inputEffects.size());
85    return m_inputEffects.at(number).get();
86}
87
88void FilterEffect::clearResult()
89{
90    if (m_imageBufferResult)
91        m_imageBufferResult.clear();
92    if (m_unmultipliedImageResult)
93        m_unmultipliedImageResult.clear();
94    if (m_premultipliedImageResult)
95        m_premultipliedImageResult.clear();
96}
97
98ImageBuffer* FilterEffect::asImageBuffer()
99{
100    if (!hasResult())
101        return 0;
102    if (m_imageBufferResult)
103        return m_imageBufferResult.get();
104    m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), ColorSpaceLinearRGB);
105    IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
106    if (m_premultipliedImageResult)
107        m_imageBufferResult->putPremultipliedImageData(m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
108    else
109        m_imageBufferResult->putUnmultipliedImageData(m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
110    return m_imageBufferResult.get();
111}
112
113PassRefPtr<ByteArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
114{
115    ASSERT(isFilterSizeValid(rect));
116    RefPtr<ByteArray> imageData = ByteArray::create(rect.width() * rect.height() * 4);
117    copyUnmultipliedImage(imageData.get(), rect);
118    return imageData.release();
119}
120
121PassRefPtr<ByteArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
122{
123    ASSERT(isFilterSizeValid(rect));
124    RefPtr<ByteArray> imageData = ByteArray::create(rect.width() * rect.height() * 4);
125    copyPremultipliedImage(imageData.get(), rect);
126    return imageData.release();
127}
128
129inline void FilterEffect::copyImageBytes(ByteArray* source, ByteArray* destination, const IntRect& rect)
130{
131    // Initialize the destination to transparent black, if not entirely covered by the source.
132    if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height())
133        memset(destination->data(), 0, destination->length());
134
135    // Early return if the rect does not intersect with the source.
136    if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height())
137        return;
138
139    int xOrigin = rect.x();
140    int xDest = 0;
141    if (xOrigin < 0) {
142        xDest = -xOrigin;
143        xOrigin = 0;
144    }
145    int xEnd = rect.maxX();
146    if (xEnd > m_absolutePaintRect.width())
147        xEnd = m_absolutePaintRect.width();
148
149    int yOrigin = rect.y();
150    int yDest = 0;
151    if (yOrigin < 0) {
152        yDest = -yOrigin;
153        yOrigin = 0;
154    }
155    int yEnd = rect.maxY();
156    if (yEnd > m_absolutePaintRect.height())
157        yEnd = m_absolutePaintRect.height();
158
159    int size = (xEnd - xOrigin) * 4;
160    int destinationScanline = rect.width() * 4;
161    int sourceScanline = m_absolutePaintRect.width() * 4;
162    unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4;
163    unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4;
164
165    while (yOrigin < yEnd) {
166        memcpy(destinationPixel, sourcePixel, size);
167        destinationPixel += destinationScanline;
168        sourcePixel += sourceScanline;
169        ++yOrigin;
170    }
171}
172
173void FilterEffect::copyUnmultipliedImage(ByteArray* destination, const IntRect& rect)
174{
175    ASSERT(hasResult());
176
177    if (!m_unmultipliedImageResult) {
178        // We prefer a conversion from the image buffer.
179        if (m_imageBufferResult)
180            m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
181        else {
182            ASSERT(isFilterSizeValid(m_absolutePaintRect));
183            m_unmultipliedImageResult = ByteArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
184            unsigned char* sourceComponent = m_premultipliedImageResult->data();
185            unsigned char* destinationComponent = m_unmultipliedImageResult->data();
186            unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
187            while (sourceComponent < end) {
188                int alpha = sourceComponent[3];
189                if (alpha) {
190                    destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
191                    destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
192                    destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
193                } else {
194                    destinationComponent[0] = 0;
195                    destinationComponent[1] = 0;
196                    destinationComponent[2] = 0;
197                }
198                destinationComponent[3] = alpha;
199                sourceComponent += 4;
200                destinationComponent += 4;
201            }
202        }
203    }
204    copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
205}
206
207void FilterEffect::copyPremultipliedImage(ByteArray* destination, const IntRect& rect)
208{
209    ASSERT(hasResult());
210
211    if (!m_premultipliedImageResult) {
212        // We prefer a conversion from the image buffer.
213        if (m_imageBufferResult)
214            m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
215        else {
216            ASSERT(isFilterSizeValid(m_absolutePaintRect));
217            m_premultipliedImageResult = ByteArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
218            unsigned char* sourceComponent = m_unmultipliedImageResult->data();
219            unsigned char* destinationComponent = m_premultipliedImageResult->data();
220            unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
221            while (sourceComponent < end) {
222                int alpha = sourceComponent[3];
223                destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
224                destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
225                destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
226                destinationComponent[3] = alpha;
227                sourceComponent += 4;
228                destinationComponent += 4;
229            }
230        }
231    }
232    copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
233}
234
235ImageBuffer* FilterEffect::createImageBufferResult()
236{
237    // Only one result type is allowed.
238    ASSERT(!hasResult());
239    determineAbsolutePaintRect();
240    if (m_absolutePaintRect.isEmpty())
241        return 0;
242    m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), ColorSpaceLinearRGB);
243    if (!m_imageBufferResult)
244        return 0;
245    ASSERT(m_imageBufferResult->context());
246    return m_imageBufferResult.get();
247}
248
249ByteArray* FilterEffect::createUnmultipliedImageResult()
250{
251    // Only one result type is allowed.
252    ASSERT(!hasResult());
253    ASSERT(isFilterSizeValid(m_absolutePaintRect));
254
255    determineAbsolutePaintRect();
256    if (m_absolutePaintRect.isEmpty())
257        return 0;
258    m_unmultipliedImageResult = ByteArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
259    return m_unmultipliedImageResult.get();
260}
261
262ByteArray* FilterEffect::createPremultipliedImageResult()
263{
264    // Only one result type is allowed.
265    ASSERT(!hasResult());
266    ASSERT(isFilterSizeValid(m_absolutePaintRect));
267
268    determineAbsolutePaintRect();
269    if (m_absolutePaintRect.isEmpty())
270        return 0;
271    m_premultipliedImageResult = ByteArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
272    return m_premultipliedImageResult.get();
273}
274
275TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
276{
277    // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
278    // possible at the moment, because we need more detailed informations from the target object.
279    return ts;
280}
281
282} // namespace WebCore
283
284#endif // ENABLE(FILTERS)
285