| /* |
| * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> |
| * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> |
| * Copyright (C) 2005 Eric Seidel <eric@webkit.org> |
| * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> |
| * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
| * Copyright (C) 2021 Apple Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "FECompositeSoftwareApplier.h" |
| |
| #include "FEComposite.h" |
| #include "GraphicsContext.h" |
| #include "ImageBuffer.h" |
| #include "PixelBuffer.h" |
| #include <wtf/MathExtras.h> |
| |
| namespace WebCore { |
| |
| uint8_t FECompositeSoftwareApplier::clampByte(int c) |
| { |
| uint8_t buff[] = { static_cast<uint8_t>(c), 255, 0 }; |
| unsigned uc = static_cast<unsigned>(c); |
| return buff[!!(uc & ~0xff) + !!(uc & ~(~0u >> 1))]; |
| } |
| |
| template <int b1, int b4> |
| inline void FECompositeSoftwareApplier::computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) |
| { |
| float scaledK1; |
| float scaledK4; |
| if (b1) |
| scaledK1 = k1 / 255.0f; |
| if (b4) |
| scaledK4 = k4 * 255.0f; |
| |
| while (--pixelArrayLength >= 0) { |
| unsigned char i1 = *source; |
| unsigned char i2 = *destination; |
| float result = k2 * i1 + k3 * i2; |
| if (b1) |
| result += scaledK1 * i1 * i2; |
| if (b4) |
| result += scaledK4; |
| |
| *destination = clampByte(result); |
| ++source; |
| ++destination; |
| } |
| } |
| |
| // computeArithmeticPixelsUnclamped is a faster version of computeArithmeticPixels for the common case where clamping |
| // is not necessary. This enables aggresive compiler optimizations such as auto-vectorization. |
| template <int b1, int b4> |
| inline void FECompositeSoftwareApplier::computeArithmeticPixelsUnclamped(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) |
| { |
| float scaledK1; |
| float scaledK4; |
| if (b1) |
| scaledK1 = k1 / 255.0f; |
| if (b4) |
| scaledK4 = k4 * 255.0f; |
| |
| while (--pixelArrayLength >= 0) { |
| unsigned char i1 = *source; |
| unsigned char i2 = *destination; |
| float result = k2 * i1 + k3 * i2; |
| if (b1) |
| result += scaledK1 * i1 * i2; |
| if (b4) |
| result += scaledK4; |
| |
| *destination = result; |
| ++source; |
| ++destination; |
| } |
| } |
| |
| #if !HAVE(ARM_NEON_INTRINSICS) |
| inline void FECompositeSoftwareApplier::applyPlatformArithmetic(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) |
| { |
| float upperLimit = std::max(0.0f, k1) + std::max(0.0f, k2) + std::max(0.0f, k3) + k4; |
| float lowerLimit = std::min(0.0f, k1) + std::min(0.0f, k2) + std::min(0.0f, k3) + k4; |
| if ((k4 >= 0.0f && k4 <= 1.0f) && (upperLimit >= 0.0f && upperLimit <= 1.0f) && (lowerLimit >= 0.0f && lowerLimit <= 1.0f)) { |
| if (k4) { |
| if (k1) |
| computeArithmeticPixelsUnclamped<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); |
| else |
| computeArithmeticPixelsUnclamped<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); |
| } else { |
| if (k1) |
| computeArithmeticPixelsUnclamped<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); |
| else |
| computeArithmeticPixelsUnclamped<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); |
| } |
| return; |
| } |
| |
| if (k4) { |
| if (k1) |
| computeArithmeticPixels<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); |
| else |
| computeArithmeticPixels<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); |
| } else { |
| if (k1) |
| computeArithmeticPixels<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); |
| else |
| computeArithmeticPixels<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); |
| } |
| } |
| #endif |
| |
| bool FECompositeSoftwareApplier::applyArithmetic(FilterImage& input, FilterImage& input2, FilterImage& result) |
| { |
| auto destinationPixelBuffer = result.pixelBuffer(AlphaPremultiplication::Premultiplied); |
| if (!destinationPixelBuffer) |
| return false; |
| |
| IntRect effectADrawingRect = m_effect.requestedRegionOfInputPixelBuffer(input.absoluteImageRect()); |
| auto sourcePixelBuffer = input.getPixelBuffer(AlphaPremultiplication::Premultiplied, effectADrawingRect, m_effect.operatingColorSpace()); |
| if (!sourcePixelBuffer) |
| return false; |
| |
| IntRect effectBDrawingRect = m_effect.requestedRegionOfInputPixelBuffer(input2.absoluteImageRect()); |
| input2.copyPixelBuffer(*destinationPixelBuffer, effectBDrawingRect); |
| |
| auto& sourcePixelArray = sourcePixelBuffer->data(); |
| auto& destinationPixelArray = destinationPixelBuffer->data(); |
| |
| int length = sourcePixelArray.length(); |
| ASSERT(length == static_cast<int>(destinationPixelArray.length())); |
| applyPlatformArithmetic(sourcePixelArray.data(), destinationPixelArray.data(), length, m_effect.k1(), m_effect.k2(), m_effect.k3(), m_effect.k4()); |
| return true; |
| } |
| |
| bool FECompositeSoftwareApplier::applyNonArithmetic(FilterImage& input, FilterImage& input2, FilterImage& result) |
| { |
| auto resultImage = result.imageBuffer(); |
| if (!resultImage) |
| return false; |
| |
| auto inputImage = input.imageBuffer(); |
| auto inputImage2 = input2.imageBuffer(); |
| if (!inputImage || !inputImage2) |
| return false; |
| |
| auto& filterContext = resultImage->context(); |
| |
| switch (m_effect.operation()) { |
| case FECOMPOSITE_OPERATOR_UNKNOWN: |
| return false; |
| |
| case FECOMPOSITE_OPERATOR_OVER: |
| filterContext.drawImageBuffer(*inputImage2, m_effect.drawingRegionOfInputImage(input2.absoluteImageRect())); |
| filterContext.drawImageBuffer(*inputImage, m_effect.drawingRegionOfInputImage(input.absoluteImageRect())); |
| break; |
| |
| case FECOMPOSITE_OPERATOR_IN: { |
| // Applies only to the intersected region. |
| IntRect destinationRect = input.absoluteImageRect(); |
| destinationRect.intersect(input2.absoluteImageRect()); |
| destinationRect.intersect(result.absoluteImageRect()); |
| if (destinationRect.isEmpty()) |
| break; |
| IntRect adjustedDestinationRect = destinationRect - result.absoluteImageRect().location(); |
| IntRect sourceRect = destinationRect - input.absoluteImageRect().location(); |
| IntRect source2Rect = destinationRect - input2.absoluteImageRect().location(); |
| filterContext.drawImageBuffer(*inputImage2, FloatRect(adjustedDestinationRect), FloatRect(source2Rect)); |
| filterContext.drawImageBuffer(*inputImage, FloatRect(adjustedDestinationRect), FloatRect(sourceRect), { CompositeOperator::SourceIn }); |
| break; |
| } |
| |
| case FECOMPOSITE_OPERATOR_OUT: |
| filterContext.drawImageBuffer(*inputImage, m_effect.drawingRegionOfInputImage(input.absoluteImageRect())); |
| filterContext.drawImageBuffer(*inputImage2, m_effect.drawingRegionOfInputImage(input2.absoluteImageRect()), { { }, inputImage2->logicalSize() }, CompositeOperator::DestinationOut); |
| break; |
| |
| case FECOMPOSITE_OPERATOR_ATOP: |
| filterContext.drawImageBuffer(*inputImage2, m_effect.drawingRegionOfInputImage(input2.absoluteImageRect())); |
| filterContext.drawImageBuffer(*inputImage, m_effect.drawingRegionOfInputImage(input.absoluteImageRect()), { { }, inputImage->logicalSize() }, CompositeOperator::SourceAtop); |
| break; |
| |
| case FECOMPOSITE_OPERATOR_XOR: |
| filterContext.drawImageBuffer(*inputImage2, m_effect.drawingRegionOfInputImage(input2.absoluteImageRect())); |
| filterContext.drawImageBuffer(*inputImage, m_effect.drawingRegionOfInputImage(input.absoluteImageRect()), { { }, inputImage->logicalSize() }, CompositeOperator::XOR); |
| break; |
| |
| case FECOMPOSITE_OPERATOR_ARITHMETIC: |
| ASSERT_NOT_REACHED(); |
| return false; |
| |
| case FECOMPOSITE_OPERATOR_LIGHTER: |
| filterContext.drawImageBuffer(*inputImage2, m_effect.drawingRegionOfInputImage(input2.absoluteImageRect())); |
| filterContext.drawImageBuffer(*inputImage, m_effect.drawingRegionOfInputImage(input.absoluteImageRect()), { { }, inputImage->logicalSize() }, CompositeOperator::PlusLighter); |
| break; |
| } |
| |
| return true; |
| } |
| |
| bool FECompositeSoftwareApplier::apply(const Filter&, const FilterImageVector& inputs, FilterImage& result) |
| { |
| auto& input = inputs[0].get(); |
| auto& input2 = inputs[1].get(); |
| |
| if (m_effect.operation() == FECOMPOSITE_OPERATOR_ARITHMETIC) |
| return applyArithmetic(input, input2, result); |
| return applyNonArithmetic(input, input2, result); |
| } |
| |
| } // namespace WebCore |