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) 2012 Nokia Corporation and/or its subsidiary(-ies)
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 "FEBlend.h"
28#include "FEBlendNEON.h"
29
30#include "Filter.h"
31#include "FloatPoint.h"
32#include "GraphicsContext.h"
33#include "RenderTreeAsText.h"
34#include "TextStream.h"
35
36#include <wtf/Uint8ClampedArray.h>
37
38typedef unsigned char (*BlendType)(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB);
39
40namespace WebCore {
41
42FEBlend::FEBlend(Filter* filter, BlendModeType mode)
43    : FilterEffect(filter)
44    , m_mode(mode)
45{
46}
47
48PassRefPtr<FEBlend> FEBlend::create(Filter* filter, BlendModeType mode)
49{
50    return adoptRef(new FEBlend(filter, mode));
51}
52
53BlendModeType FEBlend::blendMode() const
54{
55    return m_mode;
56}
57
58bool FEBlend::setBlendMode(BlendModeType mode)
59{
60    if (m_mode == mode)
61        return false;
62    m_mode = mode;
63    return true;
64}
65
66inline unsigned char feBlendNormal(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char)
67{
68    return fastDivideBy255((255 - alphaA) * colorB + colorA * 255);
69}
70
71inline unsigned char feBlendMultiply(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB)
72{
73    return fastDivideBy255((255 - alphaA) * colorB + (255 - alphaB + colorB) * colorA);
74}
75
76inline unsigned char feBlendScreen(unsigned char colorA, unsigned char colorB, unsigned char, unsigned char)
77{
78    return fastDivideBy255((colorB + colorA) * 255 - colorA * colorB);
79}
80
81inline unsigned char feBlendDarken(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB)
82{
83    return fastDivideBy255(std::min((255 - alphaA) * colorB + colorA * 255, (255 - alphaB) * colorA + colorB * 255));
84}
85
86inline unsigned char feBlendLighten(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB)
87{
88    return fastDivideBy255(std::max((255 - alphaA) * colorB + colorA * 255, (255 - alphaB) * colorA + colorB * 255));
89}
90
91inline unsigned char feBlendUnknown(unsigned char, unsigned char, unsigned char, unsigned char)
92{
93    return 0;
94}
95
96template<BlendType BlendFunction>
97static void platformApply(unsigned char* sourcePixelA, unsigned char* sourcePixelB,
98                          unsigned char* destinationPixel, unsigned pixelArrayLength)
99{
100    unsigned len = pixelArrayLength / 4;
101    for (unsigned pixelOffset = 0; pixelOffset < len; pixelOffset++) {
102        unsigned char alphaA = sourcePixelA[3];
103        unsigned char alphaB = sourcePixelB[3];
104        destinationPixel[0] = BlendFunction(sourcePixelA[0], sourcePixelB[0], alphaA, alphaB);
105        destinationPixel[1] = BlendFunction(sourcePixelA[1], sourcePixelB[1], alphaA, alphaB);
106        destinationPixel[2] = BlendFunction(sourcePixelA[2], sourcePixelB[2], alphaA, alphaB);
107        destinationPixel[3] = 255 - fastDivideBy255((255 - alphaA) * (255 - alphaB));
108        sourcePixelA += 4;
109        sourcePixelB += 4;
110        destinationPixel += 4;
111    }
112}
113
114void FEBlend::platformApplyGeneric(unsigned char* sourcePixelA, unsigned char* sourcePixelB,
115                                   unsigned char* destinationPixel, unsigned pixelArrayLength)
116{
117    switch (m_mode) {
118    case FEBLEND_MODE_NORMAL:
119        platformApply<feBlendNormal>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
120        break;
121    case FEBLEND_MODE_MULTIPLY:
122        platformApply<feBlendMultiply>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
123        break;
124    case FEBLEND_MODE_SCREEN:
125        platformApply<feBlendScreen>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
126        break;
127    case FEBLEND_MODE_DARKEN:
128        platformApply<feBlendDarken>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
129        break;
130    case FEBLEND_MODE_LIGHTEN:
131        platformApply<feBlendLighten>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
132        break;
133    case FEBLEND_MODE_UNKNOWN:
134        platformApply<feBlendUnknown>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
135        break;
136    }
137}
138
139void FEBlend::platformApplySoftware()
140{
141    FilterEffect* in = inputEffect(0);
142    FilterEffect* in2 = inputEffect(1);
143
144    ASSERT(m_mode > FEBLEND_MODE_UNKNOWN);
145    ASSERT(m_mode <= FEBLEND_MODE_LIGHTEN);
146
147    Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult();
148    if (!dstPixelArray)
149        return;
150
151    IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
152    RefPtr<Uint8ClampedArray> srcPixelArrayA = in->asPremultipliedImage(effectADrawingRect);
153
154    IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect());
155    RefPtr<Uint8ClampedArray> srcPixelArrayB = in2->asPremultipliedImage(effectBDrawingRect);
156
157    unsigned pixelArrayLength = srcPixelArrayA->length();
158    ASSERT(pixelArrayLength == srcPixelArrayB->length());
159
160#if HAVE(ARM_NEON_INTRINSICS)
161    if (pixelArrayLength >= 8)
162        platformApplyNEON(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength);
163    else { // If there is just one pixel we expand it to two.
164        ASSERT(pixelArrayLength > 0);
165        uint32_t sourceA[2] = {0, 0};
166        uint32_t sourceBAndDest[2] = {0, 0};
167
168        sourceA[0] = reinterpret_cast<uint32_t*>(srcPixelArrayA->data())[0];
169        sourceBAndDest[0] = reinterpret_cast<uint32_t*>(srcPixelArrayB->data())[0];
170        platformApplyNEON(reinterpret_cast<uint8_t*>(sourceA), reinterpret_cast<uint8_t*>(sourceBAndDest), reinterpret_cast<uint8_t*>(sourceBAndDest), 8);
171        reinterpret_cast<uint32_t*>(dstPixelArray->data())[0] = sourceBAndDest[0];
172    }
173#else
174    platformApplyGeneric(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength);
175#endif
176}
177
178void FEBlend::dump()
179{
180}
181
182static TextStream& operator<<(TextStream& ts, const BlendModeType& type)
183{
184    switch (type) {
185    case FEBLEND_MODE_UNKNOWN:
186        ts << "UNKNOWN";
187        break;
188    case FEBLEND_MODE_NORMAL:
189        ts << "NORMAL";
190        break;
191    case FEBLEND_MODE_MULTIPLY:
192        ts << "MULTIPLY";
193        break;
194    case FEBLEND_MODE_SCREEN:
195        ts << "SCREEN";
196        break;
197    case FEBLEND_MODE_DARKEN:
198        ts << "DARKEN";
199        break;
200    case FEBLEND_MODE_LIGHTEN:
201        ts << "LIGHTEN";
202        break;
203    }
204    return ts;
205}
206
207TextStream& FEBlend::externalRepresentation(TextStream& ts, int indent) const
208{
209    writeIndent(ts, indent);
210    ts << "[feBlend";
211    FilterEffect::externalRepresentation(ts);
212    ts << " mode=\"" << m_mode << "\"]\n";
213    inputEffect(0)->externalRepresentation(ts, indent + 1);
214    inputEffect(1)->externalRepresentation(ts, indent + 1);
215    return ts;
216}
217
218} // namespace WebCore
219
220#endif // ENABLE(FILTERS)
221