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 * 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
27#include "platform/graphics/filters/FEComposite.h"
28
29#include "SkArithmeticMode.h"
30#include "SkXfermodeImageFilter.h"
31
32#include "platform/graphics/GraphicsContext.h"
33#include "platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h"
34#include "platform/graphics/filters/SkiaImageFilterBuilder.h"
35#include "platform/text/TextStream.h"
36#include "third_party/skia/include/core/SkDevice.h"
37
38#include "wtf/Uint8ClampedArray.h"
39
40namespace blink {
41
42FEComposite::FEComposite(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
43    : FilterEffect(filter)
44    , m_type(type)
45    , m_k1(k1)
46    , m_k2(k2)
47    , m_k3(k3)
48    , m_k4(k4)
49{
50}
51
52PassRefPtr<FEComposite> FEComposite::create(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4)
53{
54    return adoptRef(new FEComposite(filter, type, k1, k2, k3, k4));
55}
56
57CompositeOperationType FEComposite::operation() const
58{
59    return m_type;
60}
61
62bool FEComposite::setOperation(CompositeOperationType type)
63{
64    if (m_type == type)
65        return false;
66    m_type = type;
67    return true;
68}
69
70float FEComposite::k1() const
71{
72    return m_k1;
73}
74
75bool FEComposite::setK1(float k1)
76{
77    if (m_k1 == k1)
78        return false;
79    m_k1 = k1;
80    return true;
81}
82
83float FEComposite::k2() const
84{
85    return m_k2;
86}
87
88bool FEComposite::setK2(float k2)
89{
90    if (m_k2 == k2)
91        return false;
92    m_k2 = k2;
93    return true;
94}
95
96float FEComposite::k3() const
97{
98    return m_k3;
99}
100
101bool FEComposite::setK3(float k3)
102{
103    if (m_k3 == k3)
104        return false;
105    m_k3 = k3;
106    return true;
107}
108
109float FEComposite::k4() const
110{
111    return m_k4;
112}
113
114bool FEComposite::setK4(float k4)
115{
116    if (m_k4 == k4)
117        return false;
118    m_k4 = k4;
119    return true;
120}
121
122void FEComposite::correctFilterResultIfNeeded()
123{
124    if (m_type != FECOMPOSITE_OPERATOR_ARITHMETIC)
125        return;
126
127    forceValidPreMultipliedPixels();
128}
129
130template <int b1, int b4>
131static inline void computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength,
132                                    float k1, float k2, float k3, float k4)
133{
134    float scaledK1;
135    float scaledK4;
136    if (b1)
137        scaledK1 = k1 / 255.0f;
138    if (b4)
139        scaledK4 = k4 * 255.0f;
140
141    while (--pixelArrayLength >= 0) {
142        unsigned char i1 = *source;
143        unsigned char i2 = *destination;
144        float result = k2 * i1 + k3 * i2;
145        if (b1)
146            result += scaledK1 * i1 * i2;
147        if (b4)
148            result += scaledK4;
149
150        if (result <= 0)
151            *destination = 0;
152        else if (result >= 255)
153            *destination = 255;
154        else
155            *destination = result;
156        ++source;
157        ++destination;
158    }
159}
160
161// computeArithmeticPixelsUnclamped is a faster version of computeArithmeticPixels for the common case where clamping
162// is not necessary. This enables aggresive compiler optimizations such as auto-vectorization.
163template <int b1, int b4>
164static inline void computeArithmeticPixelsUnclamped(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4)
165{
166    float scaledK1;
167    float scaledK4;
168    if (b1)
169        scaledK1 = k1 / 255.0f;
170    if (b4)
171        scaledK4 = k4 * 255.0f;
172
173    while (--pixelArrayLength >= 0) {
174        unsigned char i1 = *source;
175        unsigned char i2 = *destination;
176        float result = k2 * i1 + k3 * i2;
177        if (b1)
178            result += scaledK1 * i1 * i2;
179        if (b4)
180            result += scaledK4;
181
182        *destination = result;
183        ++source;
184        ++destination;
185    }
186}
187
188static inline void arithmeticSoftware(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4)
189{
190    float upperLimit = std::max(0.0f, k1) + std::max(0.0f, k2) + std::max(0.0f, k3) + k4;
191    float lowerLimit = std::min(0.0f, k1) + std::min(0.0f, k2) + std::min(0.0f, k3) + k4;
192    if ((k4 >= 0.0f && k4 <= 1.0f) && (upperLimit >= 0.0f && upperLimit <= 1.0f) && (lowerLimit >= 0.0f && lowerLimit <= 1.0f)) {
193        if (k4) {
194            if (k1)
195                computeArithmeticPixelsUnclamped<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
196            else
197                computeArithmeticPixelsUnclamped<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
198        } else {
199            if (k1)
200                computeArithmeticPixelsUnclamped<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
201            else
202                computeArithmeticPixelsUnclamped<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
203        }
204        return;
205    }
206
207    if (k4) {
208        if (k1)
209            computeArithmeticPixels<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
210        else
211            computeArithmeticPixels<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4);
212    } else {
213        if (k1)
214            computeArithmeticPixels<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
215        else
216            computeArithmeticPixels<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4);
217    }
218}
219
220inline void FEComposite::platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination,
221    float k1, float k2, float k3, float k4)
222{
223    int length = source->length();
224    ASSERT(length == static_cast<int>(destination->length()));
225    // The selection here eventually should happen dynamically.
226#if HAVE(ARM_NEON_INTRINSICS)
227    ASSERT(!(length & 0x3));
228    platformArithmeticNeon(source->data(), destination->data(), length, k1, k2, k3, k4);
229#else
230    arithmeticSoftware(source->data(), destination->data(), length, k1, k2, k3, k4);
231#endif
232}
233
234FloatRect FEComposite::determineAbsolutePaintRect(const FloatRect& originalRequestedRect)
235{
236    FloatRect requestedRect = originalRequestedRect;
237    if (clipsToBounds())
238        requestedRect.intersect(maxEffectRect());
239
240    // We may be called multiple times if result is used more than once. Return
241    // quickly if nothing new is required.
242    if (absolutePaintRect().contains(enclosingIntRect(requestedRect)))
243        return requestedRect;
244
245    // No mapPaintRect required for FEComposite.
246    FloatRect input1Rect = inputEffect(1)->determineAbsolutePaintRect(requestedRect);
247    FloatRect affectedRect;
248    switch (m_type) {
249    case FECOMPOSITE_OPERATOR_IN:
250        // 'in' has output only in the intersection of both inputs.
251        affectedRect = intersection(input1Rect, inputEffect(0)->determineAbsolutePaintRect(input1Rect));
252        break;
253    case FECOMPOSITE_OPERATOR_ATOP:
254        // 'atop' has output only in the extents of the second input.
255        // Make sure first input knows where it needs to produce output.
256        inputEffect(0)->determineAbsolutePaintRect(input1Rect);
257        affectedRect = input1Rect;
258        break;
259    case FECOMPOSITE_OPERATOR_ARITHMETIC:
260        if (k4() > 0) {
261            // Make sure first input knows where it needs to produce output.
262            inputEffect(0)->determineAbsolutePaintRect(requestedRect);
263            // Arithmetic with non-zero k4 may influnce the complete filter primitive
264            // region. So we can't optimize the paint region here.
265            affectedRect = requestedRect;
266            break;
267        }
268        if (k2() <= 0) {
269            // Input 0 does not appear where input 1 is not present.
270            FloatRect input0Rect = inputEffect(0)->determineAbsolutePaintRect(input1Rect);
271            if (k3() > 0) {
272                affectedRect = input1Rect;
273            } else {
274                // Just k1 is positive. Use intersection.
275                affectedRect = intersection(input1Rect, input0Rect);
276            }
277            break;
278        }
279        // else fall through to use union
280    default:
281        // Take the union of both input effects.
282        affectedRect = unionRect(input1Rect, inputEffect(0)->determineAbsolutePaintRect(requestedRect));
283        break;
284    }
285
286    affectedRect.intersect(requestedRect);
287    addAbsolutePaintRect(affectedRect);
288    return affectedRect;
289}
290
291void FEComposite::applySoftware()
292{
293    FilterEffect* in = inputEffect(0);
294    FilterEffect* in2 = inputEffect(1);
295
296    if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) {
297        Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult();
298        if (!dstPixelArray)
299            return;
300
301        IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
302        RefPtr<Uint8ClampedArray> srcPixelArray = in->asPremultipliedImage(effectADrawingRect);
303
304        IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect());
305        in2->copyPremultipliedImage(dstPixelArray, effectBDrawingRect);
306
307        platformArithmeticSoftware(srcPixelArray.get(), dstPixelArray, m_k1, m_k2, m_k3, m_k4);
308        return;
309    }
310
311    ImageBuffer* resultImage = createImageBufferResult();
312    if (!resultImage)
313        return;
314    GraphicsContext* filterContext = resultImage->context();
315
316    ImageBuffer* imageBuffer = in->asImageBuffer();
317    ImageBuffer* imageBuffer2 = in2->asImageBuffer();
318    ASSERT(imageBuffer);
319    ASSERT(imageBuffer2);
320
321    switch (m_type) {
322    case FECOMPOSITE_OPERATOR_OVER:
323        filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
324        filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
325        break;
326    case FECOMPOSITE_OPERATOR_IN: {
327        // Applies only to the intersected region.
328        IntRect destinationRect = in->absolutePaintRect();
329        destinationRect.intersect(in2->absolutePaintRect());
330        destinationRect.intersect(absolutePaintRect());
331        if (destinationRect.isEmpty())
332            break;
333        FloatRect sourceRect(IntPoint(destinationRect.x() - in->absolutePaintRect().x(),
334                                    destinationRect.y() - in->absolutePaintRect().y()), destinationRect.size());
335        FloatRect source2Rect(IntPoint(destinationRect.x() - in2->absolutePaintRect().x(),
336                                     destinationRect.y() - in2->absolutePaintRect().y()), destinationRect.size());
337        destinationRect.move(-absolutePaintRect().x(), -absolutePaintRect().y());
338        filterContext->drawImageBuffer(imageBuffer2, destinationRect, &source2Rect);
339        filterContext->drawImageBuffer(imageBuffer, destinationRect, &sourceRect, CompositeSourceIn);
340        break;
341    }
342    case FECOMPOSITE_OPERATOR_OUT:
343        filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
344        filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()), 0, CompositeDestinationOut);
345        break;
346    case FECOMPOSITE_OPERATOR_ATOP:
347        filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
348        filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), 0, CompositeSourceAtop);
349        break;
350    case FECOMPOSITE_OPERATOR_XOR:
351        filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
352        filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), 0, CompositeXOR);
353        break;
354    default:
355        break;
356    }
357}
358
359SkXfermode::Mode toXfermode(CompositeOperationType mode)
360{
361    switch (mode) {
362    case FECOMPOSITE_OPERATOR_OVER:
363        return SkXfermode::kSrcOver_Mode;
364    case FECOMPOSITE_OPERATOR_IN:
365        return SkXfermode::kSrcIn_Mode;
366    case FECOMPOSITE_OPERATOR_OUT:
367        return SkXfermode::kSrcOut_Mode;
368    case FECOMPOSITE_OPERATOR_ATOP:
369        return SkXfermode::kSrcATop_Mode;
370    case FECOMPOSITE_OPERATOR_XOR:
371        return SkXfermode::kXor_Mode;
372    default:
373        ASSERT_NOT_REACHED();
374        return SkXfermode::kSrcOver_Mode;
375    }
376}
377
378PassRefPtr<SkImageFilter> FEComposite::createImageFilter(SkiaImageFilterBuilder* builder)
379{
380    return createImageFilterInternal(builder, true);
381}
382
383PassRefPtr<SkImageFilter> FEComposite::createImageFilterWithoutValidation(SkiaImageFilterBuilder* builder)
384{
385    return createImageFilterInternal(builder, false);
386}
387
388PassRefPtr<SkImageFilter> FEComposite::createImageFilterInternal(SkiaImageFilterBuilder* builder, bool requiresPMColorValidation)
389{
390    RefPtr<SkImageFilter> foreground(builder->build(inputEffect(0), operatingColorSpace(), !mayProduceInvalidPreMultipliedPixels()));
391    RefPtr<SkImageFilter> background(builder->build(inputEffect(1), operatingColorSpace(), !mayProduceInvalidPreMultipliedPixels()));
392    SkImageFilter::CropRect cropRect = getCropRect(builder->cropOffset());
393    RefPtr<SkXfermode> mode;
394    if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC)
395        mode = adoptRef(SkArithmeticMode::Create(SkFloatToScalar(m_k1), SkFloatToScalar(m_k2), SkFloatToScalar(m_k3), SkFloatToScalar(m_k4), requiresPMColorValidation));
396    else
397        mode = adoptRef(SkXfermode::Create(toXfermode(m_type)));
398    return adoptRef(SkXfermodeImageFilter::Create(mode.get(), background.get(), foreground.get(), &cropRect));
399}
400
401static TextStream& operator<<(TextStream& ts, const CompositeOperationType& type)
402{
403    switch (type) {
404    case FECOMPOSITE_OPERATOR_UNKNOWN:
405        ts << "UNKNOWN";
406        break;
407    case FECOMPOSITE_OPERATOR_OVER:
408        ts << "OVER";
409        break;
410    case FECOMPOSITE_OPERATOR_IN:
411        ts << "IN";
412        break;
413    case FECOMPOSITE_OPERATOR_OUT:
414        ts << "OUT";
415        break;
416    case FECOMPOSITE_OPERATOR_ATOP:
417        ts << "ATOP";
418        break;
419    case FECOMPOSITE_OPERATOR_XOR:
420        ts << "XOR";
421        break;
422    case FECOMPOSITE_OPERATOR_ARITHMETIC:
423        ts << "ARITHMETIC";
424        break;
425    }
426    return ts;
427}
428
429TextStream& FEComposite::externalRepresentation(TextStream& ts, int indent) const
430{
431    writeIndent(ts, indent);
432    ts << "[feComposite";
433    FilterEffect::externalRepresentation(ts);
434    ts << " operation=\"" << m_type << "\"";
435    if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC)
436        ts << " k1=\"" << m_k1 << "\" k2=\"" << m_k2 << "\" k3=\"" << m_k3 << "\" k4=\"" << m_k4 << "\"";
437    ts << "]\n";
438    inputEffect(0)->externalRepresentation(ts, indent + 1);
439    inputEffect(1)->externalRepresentation(ts, indent + 1);
440    return ts;
441}
442
443} // namespace blink
444