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 * Copyright (C) 2013 Google Inc. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB.  If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25#include "config.h"
26#include "platform/graphics/filters/FEBlend.h"
27
28#include "SkBitmapSource.h"
29#include "SkXfermodeImageFilter.h"
30#include "platform/graphics/GraphicsContext.h"
31#include "platform/graphics/cpu/arm/filters/FEBlendNEON.h"
32#include "platform/graphics/filters/SkiaImageFilterBuilder.h"
33#include "platform/graphics/skia/NativeImageSkia.h"
34#include "platform/text/TextStream.h"
35#include "wtf/Uint8ClampedArray.h"
36
37typedef unsigned char (*BlendType)(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB);
38
39namespace WebCore {
40
41FEBlend::FEBlend(Filter* filter, BlendModeType mode)
42    : FilterEffect(filter)
43    , m_mode(mode)
44{
45}
46
47PassRefPtr<FEBlend> FEBlend::create(Filter* filter, BlendModeType mode)
48{
49    return adoptRef(new FEBlend(filter, mode));
50}
51
52BlendModeType FEBlend::blendMode() const
53{
54    return m_mode;
55}
56
57bool FEBlend::setBlendMode(BlendModeType mode)
58{
59    if (m_mode == mode)
60        return false;
61    m_mode = mode;
62    return true;
63}
64
65static inline unsigned char fastDivideBy255(uint16_t value)
66{
67    // This is an approximate algorithm for division by 255, but it gives accurate results for 16bit values.
68    uint16_t quotient = value >> 8;
69    uint16_t remainder = value - (quotient * 255) + 1;
70    return quotient + (remainder >> 8);
71}
72
73inline unsigned char feBlendNormal(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char)
74{
75    return fastDivideBy255((255 - alphaA) * colorB + colorA * 255);
76}
77
78inline unsigned char feBlendMultiply(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB)
79{
80    return fastDivideBy255((255 - alphaA) * colorB + (255 - alphaB + colorB) * colorA);
81}
82
83inline unsigned char feBlendScreen(unsigned char colorA, unsigned char colorB, unsigned char, unsigned char)
84{
85    return fastDivideBy255((colorB + colorA) * 255 - colorA * colorB);
86}
87
88inline unsigned char feBlendDarken(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB)
89{
90    return fastDivideBy255(std::min((255 - alphaA) * colorB + colorA * 255, (255 - alphaB) * colorA + colorB * 255));
91}
92
93inline unsigned char feBlendLighten(unsigned char colorA, unsigned char colorB, unsigned char alphaA, unsigned char alphaB)
94{
95    return fastDivideBy255(std::max((255 - alphaA) * colorB + colorA * 255, (255 - alphaB) * colorA + colorB * 255));
96}
97
98inline unsigned char feBlendUnknown(unsigned char, unsigned char, unsigned char, unsigned char)
99{
100    return 0;
101}
102
103template<BlendType BlendFunction>
104static void platformApply(unsigned char* sourcePixelA, unsigned char* sourcePixelB,
105                          unsigned char* destinationPixel, unsigned pixelArrayLength)
106{
107    unsigned len = pixelArrayLength / 4;
108    for (unsigned pixelOffset = 0; pixelOffset < len; pixelOffset++) {
109        unsigned char alphaA = sourcePixelA[3];
110        unsigned char alphaB = sourcePixelB[3];
111        destinationPixel[0] = BlendFunction(sourcePixelA[0], sourcePixelB[0], alphaA, alphaB);
112        destinationPixel[1] = BlendFunction(sourcePixelA[1], sourcePixelB[1], alphaA, alphaB);
113        destinationPixel[2] = BlendFunction(sourcePixelA[2], sourcePixelB[2], alphaA, alphaB);
114        destinationPixel[3] = 255 - fastDivideBy255((255 - alphaA) * (255 - alphaB));
115        sourcePixelA += 4;
116        sourcePixelB += 4;
117        destinationPixel += 4;
118    }
119}
120
121void FEBlend::platformApplyGeneric(unsigned char* sourcePixelA, unsigned char* sourcePixelB,
122                                   unsigned char* destinationPixel, unsigned pixelArrayLength)
123{
124    switch (m_mode) {
125    case FEBLEND_MODE_NORMAL:
126        platformApply<feBlendNormal>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
127        break;
128    case FEBLEND_MODE_MULTIPLY:
129        platformApply<feBlendMultiply>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
130        break;
131    case FEBLEND_MODE_SCREEN:
132        platformApply<feBlendScreen>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
133        break;
134    case FEBLEND_MODE_DARKEN:
135        platformApply<feBlendDarken>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
136        break;
137    case FEBLEND_MODE_LIGHTEN:
138        platformApply<feBlendLighten>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
139        break;
140    case FEBLEND_MODE_UNKNOWN:
141        platformApply<feBlendUnknown>(sourcePixelA, sourcePixelB, destinationPixel, pixelArrayLength);
142        break;
143    }
144}
145
146void FEBlend::applySoftware()
147{
148    FilterEffect* in = inputEffect(0);
149    FilterEffect* in2 = inputEffect(1);
150
151    ASSERT(m_mode > FEBLEND_MODE_UNKNOWN);
152    ASSERT(m_mode <= FEBLEND_MODE_LIGHTEN);
153
154    Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult();
155    if (!dstPixelArray)
156        return;
157
158    IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
159    RefPtr<Uint8ClampedArray> srcPixelArrayA = in->asPremultipliedImage(effectADrawingRect);
160
161    IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect());
162    RefPtr<Uint8ClampedArray> srcPixelArrayB = in2->asPremultipliedImage(effectBDrawingRect);
163
164    unsigned pixelArrayLength = srcPixelArrayA->length();
165    ASSERT(pixelArrayLength == srcPixelArrayB->length());
166
167#if HAVE(ARM_NEON_INTRINSICS)
168    if (pixelArrayLength >= 8) {
169        platformApplyNEON(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength);
170    }
171    else { // If there is just one pixel we expand it to two.
172        ASSERT(pixelArrayLength > 0);
173        uint32_t sourceA[2] = {0, 0};
174        uint32_t sourceBAndDest[2] = {0, 0};
175
176        sourceA[0] = reinterpret_cast<uint32_t*>(srcPixelArrayA->data())[0];
177        sourceBAndDest[0] = reinterpret_cast<uint32_t*>(srcPixelArrayB->data())[0];
178        platformApplyNEON(reinterpret_cast<uint8_t*>(sourceA), reinterpret_cast<uint8_t*>(sourceBAndDest), reinterpret_cast<uint8_t*>(sourceBAndDest), 8);
179        reinterpret_cast<uint32_t*>(dstPixelArray->data())[0] = sourceBAndDest[0];
180    }
181#else
182    platformApplyGeneric(srcPixelArrayA->data(), srcPixelArrayB->data(), dstPixelArray->data(), pixelArrayLength);
183#endif
184}
185
186static SkXfermode::Mode toSkiaMode(BlendModeType mode)
187{
188    switch (mode) {
189    case FEBLEND_MODE_NORMAL:
190        return SkXfermode::kSrcOver_Mode;
191    case FEBLEND_MODE_MULTIPLY:
192        return SkXfermode::kMultiply_Mode;
193    case FEBLEND_MODE_SCREEN:
194        return SkXfermode::kScreen_Mode;
195    case FEBLEND_MODE_DARKEN:
196        return SkXfermode::kDarken_Mode;
197    case FEBLEND_MODE_LIGHTEN:
198        return SkXfermode::kLighten_Mode;
199    default:
200        return SkXfermode::kSrcOver_Mode;
201    }
202}
203
204PassRefPtr<SkImageFilter> FEBlend::createImageFilter(SkiaImageFilterBuilder* builder)
205{
206    RefPtr<SkImageFilter> foreground(builder->build(inputEffect(0), operatingColorSpace()));
207    RefPtr<SkImageFilter> background(builder->build(inputEffect(1), operatingColorSpace()));
208    SkAutoTUnref<SkXfermode> mode(SkXfermode::Create(toSkiaMode(m_mode)));
209    SkImageFilter::CropRect cropRect = getCropRect(builder->cropOffset());
210    return adoptRef(SkXfermodeImageFilter::Create(mode, background.get(), foreground.get(), &cropRect));
211}
212
213static TextStream& operator<<(TextStream& ts, const BlendModeType& type)
214{
215    switch (type) {
216    case FEBLEND_MODE_UNKNOWN:
217        ts << "UNKNOWN";
218        break;
219    case FEBLEND_MODE_NORMAL:
220        ts << "NORMAL";
221        break;
222    case FEBLEND_MODE_MULTIPLY:
223        ts << "MULTIPLY";
224        break;
225    case FEBLEND_MODE_SCREEN:
226        ts << "SCREEN";
227        break;
228    case FEBLEND_MODE_DARKEN:
229        ts << "DARKEN";
230        break;
231    case FEBLEND_MODE_LIGHTEN:
232        ts << "LIGHTEN";
233        break;
234    }
235    return ts;
236}
237
238TextStream& FEBlend::externalRepresentation(TextStream& ts, int indent) const
239{
240    writeIndent(ts, indent);
241    ts << "[feBlend";
242    FilterEffect::externalRepresentation(ts);
243    ts << " mode=\"" << m_mode << "\"]\n";
244    inputEffect(0)->externalRepresentation(ts, indent + 1);
245    inputEffect(1)->externalRepresentation(ts, indent + 1);
246    return ts;
247}
248
249} // namespace WebCore
250