• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
40 namespace blink {
41 
FEComposite(Filter * filter,const CompositeOperationType & type,float k1,float k2,float k3,float k4)42 FEComposite::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 
create(Filter * filter,const CompositeOperationType & type,float k1,float k2,float k3,float k4)52 PassRefPtr<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 
operation() const57 CompositeOperationType FEComposite::operation() const
58 {
59     return m_type;
60 }
61 
setOperation(CompositeOperationType type)62 bool FEComposite::setOperation(CompositeOperationType type)
63 {
64     if (m_type == type)
65         return false;
66     m_type = type;
67     return true;
68 }
69 
k1() const70 float FEComposite::k1() const
71 {
72     return m_k1;
73 }
74 
setK1(float k1)75 bool FEComposite::setK1(float k1)
76 {
77     if (m_k1 == k1)
78         return false;
79     m_k1 = k1;
80     return true;
81 }
82 
k2() const83 float FEComposite::k2() const
84 {
85     return m_k2;
86 }
87 
setK2(float k2)88 bool FEComposite::setK2(float k2)
89 {
90     if (m_k2 == k2)
91         return false;
92     m_k2 = k2;
93     return true;
94 }
95 
k3() const96 float FEComposite::k3() const
97 {
98     return m_k3;
99 }
100 
setK3(float k3)101 bool FEComposite::setK3(float k3)
102 {
103     if (m_k3 == k3)
104         return false;
105     m_k3 = k3;
106     return true;
107 }
108 
k4() const109 float FEComposite::k4() const
110 {
111     return m_k4;
112 }
113 
setK4(float k4)114 bool FEComposite::setK4(float k4)
115 {
116     if (m_k4 == k4)
117         return false;
118     m_k4 = k4;
119     return true;
120 }
121 
correctFilterResultIfNeeded()122 void FEComposite::correctFilterResultIfNeeded()
123 {
124     if (m_type != FECOMPOSITE_OPERATOR_ARITHMETIC)
125         return;
126 
127     forceValidPreMultipliedPixels();
128 }
129 
130 template <int b1, int b4>
computeArithmeticPixels(unsigned char * source,unsigned char * destination,int pixelArrayLength,float k1,float k2,float k3,float k4)131 static 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.
163 template <int b1, int b4>
computeArithmeticPixelsUnclamped(unsigned char * source,unsigned char * destination,int pixelArrayLength,float k1,float k2,float k3,float k4)164 static 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 
arithmeticSoftware(unsigned char * source,unsigned char * destination,int pixelArrayLength,float k1,float k2,float k3,float k4)188 static 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 
platformArithmeticSoftware(Uint8ClampedArray * source,Uint8ClampedArray * destination,float k1,float k2,float k3,float k4)220 inline 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 
determineAbsolutePaintRect(const FloatRect & originalRequestedRect)234 FloatRect 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 
applySoftware()291 void 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 
toXfermode(CompositeOperationType mode)359 SkXfermode::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 
createImageFilter(SkiaImageFilterBuilder * builder)378 PassRefPtr<SkImageFilter> FEComposite::createImageFilter(SkiaImageFilterBuilder* builder)
379 {
380     return createImageFilterInternal(builder, true);
381 }
382 
createImageFilterWithoutValidation(SkiaImageFilterBuilder * builder)383 PassRefPtr<SkImageFilter> FEComposite::createImageFilterWithoutValidation(SkiaImageFilterBuilder* builder)
384 {
385     return createImageFilterInternal(builder, false);
386 }
387 
createImageFilterInternal(SkiaImageFilterBuilder * builder,bool requiresPMColorValidation)388 PassRefPtr<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 
operator <<(TextStream & ts,const CompositeOperationType & type)401 static 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 
externalRepresentation(TextStream & ts,int indent) const429 TextStream& 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