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