Title: [286129] trunk/Source/WebCore
Revision
286129
Author
[email protected]
Date
2021-11-23 01:22:16 -0800 (Tue, 23 Nov 2021)

Log Message

[GPU Process] Refactor the FilterEffect result buffers into a new class named 'FilterImage'
https://bugs.webkit.org/show_bug.cgi?id=225088
rdar://77487760

Reviewed by Cameron McCormack.

Move the storage and the logic for managing the result of applying the
FilterEffect to its inputs to a new class named 'FilterImage'. This will
simplify the implementation of FilterEffect. It will also allow integrating
the CoreImage seamlessly and simplifying the geometry calculation.

Instead of having three ways to create the result of a FilterEffect, there
will be one way which is by calling FilterImage::create(). This call will
not create a concrete result. But requesting the ImageBuffer or a PixelBuffer
from FilterImage will make this creation happen

The default of the operating ColorSpace is sRGB. But it will be set to
linearRGB if the color interpolation of the filter effect element is
linearRGB. The only exception is the FEImage whose result has to be in
sRGB always.

The default value of the result ColorSpace is the operating ColorSpace.
The only exception is FEDisplacementMap whose result has to be in the
ColorSpace of its first input FilterEffect.

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* platform/graphics/coreimage/FilterEffectRendererCoreImage.mm:
(WebCore::FilterEffectRendererCoreImage::connectCIFilters):
* platform/graphics/cpu/arm/filters/FEBlendNEON.h:
(WebCore::FEBlend::platformApplySoftware):
* platform/graphics/filters/FEBlend.cpp:
(WebCore::FEBlend::platformApplySoftware):
* platform/graphics/filters/FEColorMatrix.cpp:
(WebCore::FEColorMatrix::platformApplySoftware):
* platform/graphics/filters/FEComponentTransfer.cpp:
(WebCore::FEComponentTransfer::platformApplySoftware):
* platform/graphics/filters/FEComposite.cpp:
(WebCore::FEComposite::platformApplySoftware):
(WebCore::FEComposite::correctFilterResultIfNeeded): Deleted.
* platform/graphics/filters/FEComposite.h:
requiresValidPreMultipliedPixels() is replaced by the opposite
mayProduceInvalidPremultipliedPixels(). Correcting the premultiplied
result will be done by FilterEffect.

* platform/graphics/filters/FEConvolveMatrix.cpp:
(WebCore::FEConvolveMatrix::platformApplySoftware):
* platform/graphics/filters/FEDisplacementMap.cpp:
(WebCore::FEDisplacementMap::resultColorSpace const):
(WebCore::FEDisplacementMap::platformApplySoftware):
(WebCore::FEDisplacementMap::setResultColorSpace): Deleted.
* platform/graphics/filters/FEDisplacementMap.h:
(WebCore::FEDisplacementMap::xChannelIndex const):
(WebCore::FEDisplacementMap::yChannelIndex const):
* platform/graphics/filters/FEDropShadow.cpp:
(WebCore::FEDropShadow::platformApplySoftware):
* platform/graphics/filters/FEFlood.cpp:
(WebCore::FEFlood::platformApplySoftware):
* platform/graphics/filters/FEFlood.h:
* platform/graphics/filters/FEGaussianBlur.cpp:
(WebCore::FEGaussianBlur::platformApplySoftware):
* platform/graphics/filters/FELighting.cpp:
(WebCore::FELighting::platformApplySoftware):
* platform/graphics/filters/FEMerge.cpp:
(WebCore::FEMerge::platformApplySoftware):
* platform/graphics/filters/FEMorphology.cpp:
(WebCore::FEMorphology::platformApplySoftware):
* platform/graphics/filters/FEOffset.cpp:
(WebCore::FEOffset::platformApplySoftware):
* platform/graphics/filters/FETile.cpp:
(WebCore::FETile::platformApplySoftware):
* platform/graphics/filters/FETurbulence.cpp:
(WebCore::FETurbulence::platformApplySoftware):
* platform/graphics/filters/FilterEffect.cpp:
(WebCore::FilterEffect::apply):
(WebCore::FilterEffect::createResult):

(WebCore::FilterEffect::clearResult):
(WebCore::FilterEffect::clearResultsRecursive):
Clearing the result can be done by nullifying a single pointer.

(WebCore::FilterEffect::imageBufferResult):
(WebCore::FilterEffect::unpremultipliedResult):
(WebCore::FilterEffect::premultipliedResult):
(WebCore::FilterEffect::getUnpremultipliedResult):
(WebCore::FilterEffect::getPremultipliedResult):
(WebCore::FilterEffect::copyUnpremultipliedResult const):
(WebCore::FilterEffect::copyPremultipliedResult const):
(WebCore::FilterEffect::correctPremultipliedResultIfNeeded):
(WebCore::FilterEffect::transformResultColorSpace):
(WebCore::FilterEffect::externalRepresentation const):
(WebCore::FilterEffect::forceValidPreMultipliedPixels): Deleted.
(WebCore::FilterEffect::unmultipliedResult): Deleted.
(WebCore::FilterEffect::copyImageBytes const): Deleted.
(WebCore::copyPremultiplyingAlpha): Deleted.
(WebCore::copyUnpremultiplyingAlpha): Deleted.
(WebCore::FilterEffect::convertPixelBufferToColorSpace): Deleted.
(WebCore::FilterEffect::convertImageBufferToColorSpace): Deleted.
(WebCore::FilterEffect::copyConvertedImageBufferToDestination): Deleted.
(WebCore::FilterEffect::copyConvertedPixelBufferToDestination): Deleted.
(WebCore::FilterEffect::copyUnmultipliedResult): Deleted.
(WebCore::FilterEffect::copyPremultipliedResult): Deleted.
(WebCore::FilterEffect::createImageBufferResult): Deleted.
(WebCore::FilterEffect::createUnmultipliedImageResult): Deleted.
(WebCore::FilterEffect::createPremultipliedImageResult): Deleted.
(WebCore::FilterEffect::requiresPixelBufferColorSpaceConversion): Deleted.
All the logic of these functions was moved to FilterImage.cpp.

* platform/graphics/filters/FilterEffect.h:
(WebCore::FilterEffect::hasResult const):
(WebCore::FilterEffect::resultColorSpace const):
(WebCore::FilterEffect::mayProduceInvalidPremultipliedPixels const):
(WebCore::FilterEffect::correctFilterResultIfNeeded): Deleted.
(WebCore::FilterEffect::setResultColorSpace): Deleted.
(WebCore::FilterEffect::requiresValidPreMultipliedPixels): Deleted.
* platform/graphics/filters/FilterImage.cpp: Added.
(WebCore::FilterImage::create):
(WebCore::FilterImage::FilterImage):
(WebCore::FilterImage::imageBuffer):
(WebCore::copyPremultiplyingAlpha):
(WebCore::copyUnpremultiplyingAlpha):
(WebCore::FilterImage::pixelBufferIfExists):
(WebCore::FilterImage::pixelBuffer):
(WebCore::FilterImage::getPixelBuffer):
(WebCore::FilterImage::requiresPixelBufferColorSpaceConversion const):
(WebCore::FilterImage::copyImageBytes const):
(WebCore::FilterImage::getConvertedPixelBuffer const):
(WebCore::FilterImage::copyPixelBuffer):
(WebCore::FilterImage::correctPremultipliedPixelBuffer):
(WebCore::FilterImage::transformToColorSpace):
* platform/graphics/filters/FilterImage.h: Added.
(WebCore::FilterImage::absoluteImageRect const):
(WebCore::FilterImage::renderingMode const):
(WebCore::FilterImage::colorSpace const):
(WebCore::FilterImage::imageBufferIfExists):
* platform/graphics/filters/SourceAlpha.cpp:
(WebCore::SourceAlpha::platformApplySoftware):
* platform/graphics/filters/SourceGraphic.cpp:
(WebCore::SourceGraphic::platformApplySoftware):
* platform/graphics/filters/SourceGraphic.h:
(WebCore::SourceGraphic::SourceGraphic):
* rendering/svg/RenderSVGResourceFilter.cpp:
(WebCore::RenderSVGResourceFilter::postApplyResource):
* svg/graphics/filters/SVGFEImage.cpp:
(WebCore::FEImage::platformApplySoftware):
* svg/graphics/filters/SVGFEImage.h:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (286128 => 286129)


--- trunk/Source/WebCore/ChangeLog	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/ChangeLog	2021-11-23 09:22:16 UTC (rev 286129)
@@ -1,3 +1,152 @@
+2021-11-23  Said Abou-Hallawa  <[email protected]>
+
+        [GPU Process] Refactor the FilterEffect result buffers into a new class named 'FilterImage'
+        https://bugs.webkit.org/show_bug.cgi?id=225088
+        rdar://77487760
+
+        Reviewed by Cameron McCormack.
+
+        Move the storage and the logic for managing the result of applying the
+        FilterEffect to its inputs to a new class named 'FilterImage'. This will
+        simplify the implementation of FilterEffect. It will also allow integrating
+        the CoreImage seamlessly and simplifying the geometry calculation.
+
+        Instead of having three ways to create the result of a FilterEffect, there
+        will be one way which is by calling FilterImage::create(). This call will
+        not create a concrete result. But requesting the ImageBuffer or a PixelBuffer
+        from FilterImage will make this creation happen
+
+        The default of the operating ColorSpace is sRGB. But it will be set to
+        linearRGB if the color interpolation of the filter effect element is
+        linearRGB. The only exception is the FEImage whose result has to be in
+        sRGB always.
+
+        The default value of the result ColorSpace is the operating ColorSpace.
+        The only exception is FEDisplacementMap whose result has to be in the
+        ColorSpace of its first input FilterEffect.
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/graphics/coreimage/FilterEffectRendererCoreImage.mm:
+        (WebCore::FilterEffectRendererCoreImage::connectCIFilters):
+        * platform/graphics/cpu/arm/filters/FEBlendNEON.h:
+        (WebCore::FEBlend::platformApplySoftware):
+        * platform/graphics/filters/FEBlend.cpp:
+        (WebCore::FEBlend::platformApplySoftware):
+        * platform/graphics/filters/FEColorMatrix.cpp:
+        (WebCore::FEColorMatrix::platformApplySoftware):
+        * platform/graphics/filters/FEComponentTransfer.cpp:
+        (WebCore::FEComponentTransfer::platformApplySoftware):
+        * platform/graphics/filters/FEComposite.cpp:
+        (WebCore::FEComposite::platformApplySoftware):
+        (WebCore::FEComposite::correctFilterResultIfNeeded): Deleted.
+        * platform/graphics/filters/FEComposite.h:
+        requiresValidPreMultipliedPixels() is replaced by the opposite
+        mayProduceInvalidPremultipliedPixels(). Correcting the premultiplied
+        result will be done by FilterEffect.
+
+        * platform/graphics/filters/FEConvolveMatrix.cpp:
+        (WebCore::FEConvolveMatrix::platformApplySoftware):
+        * platform/graphics/filters/FEDisplacementMap.cpp:
+        (WebCore::FEDisplacementMap::resultColorSpace const):
+        (WebCore::FEDisplacementMap::platformApplySoftware):
+        (WebCore::FEDisplacementMap::setResultColorSpace): Deleted.
+        * platform/graphics/filters/FEDisplacementMap.h:
+        (WebCore::FEDisplacementMap::xChannelIndex const):
+        (WebCore::FEDisplacementMap::yChannelIndex const):
+        * platform/graphics/filters/FEDropShadow.cpp:
+        (WebCore::FEDropShadow::platformApplySoftware):
+        * platform/graphics/filters/FEFlood.cpp:
+        (WebCore::FEFlood::platformApplySoftware):
+        * platform/graphics/filters/FEFlood.h:
+        * platform/graphics/filters/FEGaussianBlur.cpp:
+        (WebCore::FEGaussianBlur::platformApplySoftware):
+        * platform/graphics/filters/FELighting.cpp:
+        (WebCore::FELighting::platformApplySoftware):
+        * platform/graphics/filters/FEMerge.cpp:
+        (WebCore::FEMerge::platformApplySoftware):
+        * platform/graphics/filters/FEMorphology.cpp:
+        (WebCore::FEMorphology::platformApplySoftware):
+        * platform/graphics/filters/FEOffset.cpp:
+        (WebCore::FEOffset::platformApplySoftware):
+        * platform/graphics/filters/FETile.cpp:
+        (WebCore::FETile::platformApplySoftware):
+        * platform/graphics/filters/FETurbulence.cpp:
+        (WebCore::FETurbulence::platformApplySoftware):
+        * platform/graphics/filters/FilterEffect.cpp:
+        (WebCore::FilterEffect::apply):
+        (WebCore::FilterEffect::createResult):
+
+        (WebCore::FilterEffect::clearResult):
+        (WebCore::FilterEffect::clearResultsRecursive):
+        Clearing the result can be done by nullifying a single pointer.
+
+        (WebCore::FilterEffect::imageBufferResult):
+        (WebCore::FilterEffect::unpremultipliedResult):
+        (WebCore::FilterEffect::premultipliedResult):
+        (WebCore::FilterEffect::getUnpremultipliedResult):
+        (WebCore::FilterEffect::getPremultipliedResult):
+        (WebCore::FilterEffect::copyUnpremultipliedResult const):
+        (WebCore::FilterEffect::copyPremultipliedResult const):
+        (WebCore::FilterEffect::correctPremultipliedResultIfNeeded):
+        (WebCore::FilterEffect::transformResultColorSpace):
+        (WebCore::FilterEffect::externalRepresentation const):
+        (WebCore::FilterEffect::forceValidPreMultipliedPixels): Deleted.
+        (WebCore::FilterEffect::unmultipliedResult): Deleted.
+        (WebCore::FilterEffect::copyImageBytes const): Deleted.
+        (WebCore::copyPremultiplyingAlpha): Deleted.
+        (WebCore::copyUnpremultiplyingAlpha): Deleted.
+        (WebCore::FilterEffect::convertPixelBufferToColorSpace): Deleted.
+        (WebCore::FilterEffect::convertImageBufferToColorSpace): Deleted.
+        (WebCore::FilterEffect::copyConvertedImageBufferToDestination): Deleted.
+        (WebCore::FilterEffect::copyConvertedPixelBufferToDestination): Deleted.
+        (WebCore::FilterEffect::copyUnmultipliedResult): Deleted.
+        (WebCore::FilterEffect::copyPremultipliedResult): Deleted.
+        (WebCore::FilterEffect::createImageBufferResult): Deleted.
+        (WebCore::FilterEffect::createUnmultipliedImageResult): Deleted.
+        (WebCore::FilterEffect::createPremultipliedImageResult): Deleted.
+        (WebCore::FilterEffect::requiresPixelBufferColorSpaceConversion): Deleted.
+        All the logic of these functions was moved to FilterImage.cpp.
+
+        * platform/graphics/filters/FilterEffect.h:
+        (WebCore::FilterEffect::hasResult const):
+        (WebCore::FilterEffect::resultColorSpace const):
+        (WebCore::FilterEffect::mayProduceInvalidPremultipliedPixels const):
+        (WebCore::FilterEffect::correctFilterResultIfNeeded): Deleted.
+        (WebCore::FilterEffect::setResultColorSpace): Deleted.
+        (WebCore::FilterEffect::requiresValidPreMultipliedPixels): Deleted.
+        * platform/graphics/filters/FilterImage.cpp: Added.
+        (WebCore::FilterImage::create):
+        (WebCore::FilterImage::FilterImage):
+        (WebCore::FilterImage::imageBuffer):
+        (WebCore::copyPremultiplyingAlpha):
+        (WebCore::copyUnpremultiplyingAlpha):
+        (WebCore::FilterImage::pixelBufferIfExists):
+        (WebCore::FilterImage::pixelBuffer):
+        (WebCore::FilterImage::getPixelBuffer):
+        (WebCore::FilterImage::requiresPixelBufferColorSpaceConversion const):
+        (WebCore::FilterImage::copyImageBytes const):
+        (WebCore::FilterImage::getConvertedPixelBuffer const):
+        (WebCore::FilterImage::copyPixelBuffer):
+        (WebCore::FilterImage::correctPremultipliedPixelBuffer):
+        (WebCore::FilterImage::transformToColorSpace):
+        * platform/graphics/filters/FilterImage.h: Added.
+        (WebCore::FilterImage::absoluteImageRect const):
+        (WebCore::FilterImage::renderingMode const):
+        (WebCore::FilterImage::colorSpace const):
+        (WebCore::FilterImage::imageBufferIfExists):
+        * platform/graphics/filters/SourceAlpha.cpp:
+        (WebCore::SourceAlpha::platformApplySoftware):
+        * platform/graphics/filters/SourceGraphic.cpp:
+        (WebCore::SourceGraphic::platformApplySoftware):
+        * platform/graphics/filters/SourceGraphic.h:
+        (WebCore::SourceGraphic::SourceGraphic):
+        * rendering/svg/RenderSVGResourceFilter.cpp:
+        (WebCore::RenderSVGResourceFilter::postApplyResource):
+        * svg/graphics/filters/SVGFEImage.cpp:
+        (WebCore::FEImage::platformApplySoftware):
+        * svg/graphics/filters/SVGFEImage.h:
+
 2021-11-22  Simon Fraser  <[email protected]>
 
         Have ScrollAnimator::scrollToPositionWithAnimation() take a clamping argument

Modified: trunk/Source/WebCore/Sources.txt (286128 => 286129)


--- trunk/Source/WebCore/Sources.txt	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/Sources.txt	2021-11-23 09:22:16 UTC (rev 286129)
@@ -2103,6 +2103,7 @@
 platform/graphics/filters/FilterEffect.cpp
 platform/graphics/filters/FilterEffectRenderer.cpp
 platform/graphics/filters/FilterFunction.cpp
+platform/graphics/filters/FilterImage.cpp
 platform/graphics/filters/FilterOperation.cpp
 platform/graphics/filters/FilterOperations.cpp
 platform/graphics/filters/PointLightSource.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (286128 => 286129)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-11-23 09:22:16 UTC (rev 286129)
@@ -10867,6 +10867,8 @@
 		721B496F2512AC0400FE9D3B /* ImageBitmapBacking.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ImageBitmapBacking.cpp; sourceTree = "<group>"; };
 		721B49702512AC0400FE9D3B /* ImageBitmapBacking.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageBitmapBacking.h; sourceTree = "<group>"; };
 		722A815C238FD50500C00583 /* AnimationFrameRate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnimationFrameRate.h; sourceTree = "<group>"; };
+		72435EF4273D07670005E7EE /* FilterImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FilterImage.h; sourceTree = "<group>"; };
+		72435EF5273D07670005E7EE /* FilterImage.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FilterImage.cpp; sourceTree = "<group>"; };
 		724ED3291A3A7E5400F5F13C /* EXTBlendMinMax.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EXTBlendMinMax.cpp; sourceTree = "<group>"; };
 		724ED32A1A3A7E5400F5F13C /* EXTBlendMinMax.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTBlendMinMax.h; sourceTree = "<group>"; };
 		724ED32B1A3A7E5400F5F13C /* EXTBlendMinMax.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EXTBlendMinMax.idl; sourceTree = "<group>"; };
@@ -25945,6 +25947,8 @@
 				7214B9B7274458FA003BE6DF /* FilterEffectVector.h */,
 				7262D757272A174100C56A09 /* FilterFunction.cpp */,
 				7262D756272A174100C56A09 /* FilterFunction.h */,
+				72435EF5273D07670005E7EE /* FilterImage.cpp */,
+				72435EF4273D07670005E7EE /* FilterImage.h */,
 				49ECEB631499790D00CDD3A4 /* FilterOperation.cpp */,
 				49ECEB641499790D00CDD3A4 /* FilterOperation.h */,
 				49ECEB651499790D00CDD3A4 /* FilterOperations.cpp */,

Modified: trunk/Source/WebCore/platform/graphics/coreimage/FilterEffectRendererCoreImage.mm (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/coreimage/FilterEffectRendererCoreImage.mm	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/coreimage/FilterEffectRendererCoreImage.mm	2021-11-23 09:22:16 UTC (rev 286129)
@@ -116,7 +116,6 @@
         inputImages.append(inputImage);
     }
     effect.determineAbsolutePaintRect(filter);
-    effect.setResultColorSpace(effect.operatingColorSpace());
     
     if (effect.absolutePaintRect().isEmpty() || ImageBuffer::sizeNeedsClamping(effect.absolutePaintRect().size()))
         return nullptr;

Modified: trunk/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h	2021-11-23 09:22:16 UTC (rev 286129)
@@ -111,7 +111,7 @@
     FilterEffect* in = inputEffect(0);
     FilterEffect* in2 = inputEffect(1);
 
-    auto& destinationPixelBuffer = createPremultipliedImageResult();
+    auto& destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
     if (!destinationPixelBuffer)
         return false;
 
@@ -118,10 +118,10 @@
     auto& destinationPixelArray = destinationPixelBuffer->data();
 
     IntRect effectADrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
-    auto sourcePixelArrayA = in->premultipliedResult(effectADrawingRect);
+    auto sourcePixelArrayA = in->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectADrawingRect);
 
     IntRect effectBDrawingRect = requestedRegionOfInputPixelBuffer(in2->absolutePaintRect());
-    auto sourcePixelArrayB = in2->premultipliedResult(effectBDrawingRect);
+    auto sourcePixelArrayB = in2->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectBDrawingRect);
 
     unsigned sourcePixelArrayLength = sourcePixelArrayA->length();
     ASSERT(pixelArrayLength == sourcePixelArrayB->length());

Modified: trunk/Source/WebCore/platform/graphics/filters/FEBlend.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEBlend.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEBlend.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -59,16 +59,16 @@
     FilterEffect* in = inputEffect(0);
     FilterEffect* in2 = inputEffect(1);
 
-    ImageBuffer* resultImage = createImageBufferResult();
+    auto resultImage = imageBufferResult();
     if (!resultImage)
         return false;
-    GraphicsContext& filterContext = resultImage->context();
 
-    ImageBuffer* imageBuffer = in->imageBufferResult();
-    ImageBuffer* imageBuffer2 = in2->imageBufferResult();
+    auto imageBuffer = in->imageBufferResult();
+    auto imageBuffer2 = in2->imageBufferResult();
     if (!imageBuffer || !imageBuffer2)
         return false;
 
+    GraphicsContext& filterContext = resultImage->context();
     filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
     filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), { { }, imageBuffer->logicalSize() }, { CompositeOperator::SourceOver, m_mode });
     return true;

Modified: trunk/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -277,11 +277,11 @@
 {
     FilterEffect* in = inputEffect(0);
 
-    ImageBuffer* resultImage = createImageBufferResult();
+    auto resultImage = imageBufferResult();
     if (!resultImage)
         return false;
 
-    ImageBuffer* inBuffer = in->imageBufferResult();
+    auto inBuffer = in->imageBufferResult();
     if (inBuffer)
         resultImage->context().drawImageBuffer(*inBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
 

Modified: trunk/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -105,12 +105,10 @@
 {
     FilterEffect* in = inputEffect(0);
 
-    auto& destinationPixelBuffer = createUnmultipliedImageResult();
+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Unpremultiplied);
     if (!destinationPixelBuffer)
         return false;
 
-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
     LookupTable redTable;
     LookupTable greenTable;
     LookupTable blueTable;
@@ -118,9 +116,12 @@
     computeLookupTables(redTable, greenTable, blueTable, alphaTable);
 
     IntRect drawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
-    in->copyUnmultipliedResult(destinationPixelArray, drawingRect, operatingColorSpace());
+    in->copyPixelBufferResult(*destinationPixelBuffer, drawingRect);
+    
+    auto& destinationPixelArray = destinationPixelBuffer->data();
+    uint8_t* data = ""
     unsigned destinationPixelArrayLength = destinationPixelArray.length();
-    uint8_t* data = ""
+
     for (unsigned pixelOffset = 0; pixelOffset < destinationPixelArrayLength; pixelOffset += 4) {
         data[pixelOffset] = redTable[data[pixelOffset]];
         data[pixelOffset + 1] = greenTable[data[pixelOffset + 1]];

Modified: trunk/Source/WebCore/platform/graphics/filters/FEComposite.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEComposite.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEComposite.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -87,14 +87,6 @@
     return true;
 }
 
-void FEComposite::correctFilterResultIfNeeded()
-{
-    if (m_type != FECOMPOSITE_OPERATOR_ARITHMETIC)
-        return;
-
-    forceValidPreMultipliedPixels();
-}
-    
 static unsigned char clampByte(int c)
 {
     unsigned char buff[] = { static_cast<unsigned char>(c), 255, 0 };
@@ -229,34 +221,35 @@
     FilterEffect* in2 = inputEffect(1);
 
     if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) {
-        auto& destinationPixelBuffer = createPremultipliedImageResult();
+        auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
         if (!destinationPixelBuffer)
             return false;
         
-        auto& destinationPixelArray = destinationPixelBuffer->data();
-
         IntRect effectADrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
-        auto sourcePixelArray = in->premultipliedResult(effectADrawingRect, operatingColorSpace());
-        if (!sourcePixelArray)
+        auto sourcePixelBuffer = in->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectADrawingRect, operatingColorSpace());
+        if (!sourcePixelBuffer)
             return false;
 
         IntRect effectBDrawingRect = requestedRegionOfInputPixelBuffer(in2->absolutePaintRect());
-        in2->copyPremultipliedResult(destinationPixelArray, effectBDrawingRect, operatingColorSpace());
+        in2->copyPixelBufferResult(*destinationPixelBuffer, effectBDrawingRect);
 
-        platformArithmeticSoftware(*sourcePixelArray, destinationPixelArray, m_k1, m_k2, m_k3, m_k4);
+        auto& sourcePixelArray = sourcePixelBuffer->data();
+        auto& destinationPixelArray = destinationPixelBuffer->data();
+        platformArithmeticSoftware(sourcePixelArray, destinationPixelArray, m_k1, m_k2, m_k3, m_k4);
         return true;
     }
 
-    ImageBuffer* resultImage = createImageBufferResult();
+    auto resultImage = imageBufferResult();
     if (!resultImage)
         return false;
-    GraphicsContext& filterContext = resultImage->context();
 
-    ImageBuffer* imageBuffer = in->imageBufferResult();
-    ImageBuffer* imageBuffer2 = in2->imageBufferResult();
+    auto imageBuffer = in->imageBufferResult();
+    auto imageBuffer2 = in2->imageBufferResult();
     if (!imageBuffer || !imageBuffer2)
         return false;
 
+    GraphicsContext& filterContext = resultImage->context();
+
     switch (m_type) {
     case FECOMPOSITE_OPERATOR_OVER:
         filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));

Modified: trunk/Source/WebCore/platform/graphics/filters/FEComposite.h (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEComposite.h	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEComposite.h	2021-11-23 09:22:16 UTC (rev 286129)
@@ -59,12 +59,10 @@
 private:
     FEComposite(const CompositeOperationType&, float k1, float k2, float k3, float k4);
 
-    void correctFilterResultIfNeeded() override;
+    void determineAbsolutePaintRect(const Filter&) override;
 
-    bool requiresValidPreMultipliedPixels() override { return m_type != FECOMPOSITE_OPERATOR_ARITHMETIC; }
+    bool mayProduceInvalidPremultipliedPixels() const override { return m_type == FECOMPOSITE_OPERATOR_ARITHMETIC; }
 
-    void determineAbsolutePaintRect(const Filter&) override;
-
     bool platformApplySoftware(const Filter&) override;
 
     WTF::TextStream& externalRepresentation(WTF::TextStream&, RepresentationType) const override;

Modified: trunk/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -365,26 +365,23 @@
 {
     FilterEffect* in = inputEffect(0);
 
-    auto& destinationPixelBuffer = m_preserveAlpha ? createUnmultipliedImageResult() : createPremultipliedImageResult();
+    auto alphaFormat = m_preserveAlpha ? AlphaPremultiplication::Unpremultiplied : AlphaPremultiplication::Premultiplied;
+    auto destinationPixelBuffer = pixelBufferResult(alphaFormat);
     if (!destinationPixelBuffer)
         return false;
 
-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
     IntRect effectDrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
-
-    RefPtr<Uint8ClampedArray> sourcePixelArray;
-    if (m_preserveAlpha)
-        sourcePixelArray = in->unmultipliedResult(effectDrawingRect, operatingColorSpace());
-    else
-        sourcePixelArray = in->premultipliedResult(effectDrawingRect, operatingColorSpace());
-    if (!sourcePixelArray)
+    auto sourcePixelBuffer = in->getPixelBufferResult(alphaFormat, effectDrawingRect, operatingColorSpace());
+    if (!sourcePixelBuffer)
         return false;
 
+    auto& sourcePixelArray = sourcePixelBuffer->data();
+    auto& destinationPixelArray = destinationPixelBuffer->data();
+
     IntSize paintSize = absolutePaintRect().size();
     
     PaintingData paintingData = {
-        *sourcePixelArray,
+        sourcePixelArray,
         destinationPixelArray,
         paintSize.width(),
         paintSize.height(),

Modified: trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -68,13 +68,13 @@
     return true;
 }
 
-void FEDisplacementMap::setResultColorSpace(const DestinationColorSpace&)
+const DestinationColorSpace& FEDisplacementMap::resultColorSpace() const
 {
     // Spec: The 'color-interpolation-filters' property only applies to the 'in2' source image
     // and does not apply to the 'in' source image. The 'in' source image must remain in its
     // current color space.
-    // The result is in that smae color space because it is a displacement of the 'in' image.
-    FilterEffect::setResultColorSpace(inputEffect(0)->resultColorSpace());
+    // The result is in that same color space because it is a displacement of the 'in' image.
+    return inputEffect(0)->resultColorSpace();
 }
 
 void FEDisplacementMap::transformResultColorSpace(FilterEffect* in, const int index)
@@ -98,7 +98,7 @@
     ASSERT(m_xChannelSelector != CHANNEL_UNKNOWN);
     ASSERT(m_yChannelSelector != CHANNEL_UNKNOWN);
 
-    auto& destinationPixelBuffer = createPremultipliedImageResult();
+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
     if (!destinationPixelBuffer)
         return false;
 
@@ -105,16 +105,18 @@
     auto& destinationPixelArray = destinationPixelBuffer->data();
 
     IntRect effectADrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
-    auto inputImage = in->premultipliedResult(effectADrawingRect);
+    auto inputPixelBuffer = in->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectADrawingRect);
 
     IntRect effectBDrawingRect = requestedRegionOfInputPixelBuffer(in2->absolutePaintRect());
     // The calculations using the pixel values from ‘in2’ are performed using non-premultiplied color values.
-    auto displacementImage = in2->unmultipliedResult(effectBDrawingRect);
+    auto displacementPixelBuffer = in2->getPixelBufferResult(AlphaPremultiplication::Unpremultiplied, effectBDrawingRect);
     
-    if (!inputImage || !displacementImage)
+    if (!inputPixelBuffer || !displacementPixelBuffer)
         return false;
 
-    ASSERT(inputImage->length() == displacementImage->length());
+    auto& inputImage = inputPixelBuffer->data();
+    auto& displacementImage = displacementPixelBuffer->data();
+    ASSERT(inputImage.length() == displacementImage.length());
 
     IntSize paintSize = absolutePaintRect().size();
 
@@ -135,8 +137,8 @@
         for (int x = 0; x < paintSize.width(); ++x) {
             int destinationIndex = lineStartOffset + x * 4;
             
-            int srcX = x + static_cast<int>(scaleForColorX * displacementImage->item(destinationIndex + displacementChannelX) + scaledOffsetX);
-            int srcY = y + static_cast<int>(scaleForColorY * displacementImage->item(destinationIndex + displacementChannelY) + scaledOffsetY);
+            int srcX = x + static_cast<int>(scaleForColorX * displacementImage.item(destinationIndex + displacementChannelX) + scaledOffsetX);
+            int srcY = y + static_cast<int>(scaleForColorY * displacementImage.item(destinationIndex + displacementChannelY) + scaledOffsetY);
 
             unsigned* destinationPixelPtr = reinterpret_cast<unsigned*>(destinationPixelArray.data() + destinationIndex);
             if (srcX < 0 || srcX >= paintSize.width() || srcY < 0 || srcY >= paintSize.height()) {
@@ -144,7 +146,7 @@
                 continue;
             }
 
-            *destinationPixelPtr = *reinterpret_cast<unsigned*>(inputImage->data() + byteOffsetOfPixel(srcX, srcY, rowBytes));
+            *destinationPixelPtr = *reinterpret_cast<unsigned*>(inputImage.data() + byteOffsetOfPixel(srcX, srcY, rowBytes));
         }
     }
 

Modified: trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h	2021-11-23 09:22:16 UTC (rev 286129)
@@ -47,19 +47,19 @@
     float scale() const { return m_scale; }
     bool setScale(float);
 
-    void setResultColorSpace(const DestinationColorSpace&) override;
-    void transformResultColorSpace(FilterEffect*, const int) override;
-
 private:
     FEDisplacementMap(ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float);
 
-    bool platformApplySoftware(const Filter&) override;
+    int xChannelIndex() const { return m_xChannelSelector - 1; }
+    int yChannelIndex() const { return m_yChannelSelector - 1; }
 
     void determineAbsolutePaintRect(const Filter&) override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
 
-    int xChannelIndex() const { return m_xChannelSelector - 1; }
-    int yChannelIndex() const { return m_yChannelSelector - 1; }
+    const DestinationColorSpace& resultColorSpace() const override;
+    void transformResultColorSpace(FilterEffect*, const int) override;
 
+    bool platformApplySoftware(const Filter&) override;
+
     WTF::TextStream& externalRepresentation(WTF::TextStream&, RepresentationType) const override;
 
     ChannelSelectorType m_xChannelSelector;

Modified: trunk/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -73,7 +73,7 @@
 {
     FilterEffect* in = inputEffect(0);
 
-    ImageBuffer* resultImage = createImageBufferResult();
+    auto resultImage = imageBufferResult();
     if (!resultImage)
         return false;
 
@@ -84,7 +84,7 @@
     FloatRect drawingRegionWithOffset(drawingRegion);
     drawingRegionWithOffset.move(offset);
 
-    ImageBuffer* sourceImage = in->imageBufferResult();
+    auto sourceImage = in->imageBufferResult();
     if (!sourceImage)
         return false;
 

Modified: trunk/Source/WebCore/platform/graphics/filters/FEFlood.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEFlood.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEFlood.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -60,7 +60,7 @@
 
 bool FEFlood::platformApplySoftware(const Filter&)
 {
-    ImageBuffer* resultImage = createImageBufferResult();
+    auto resultImage = imageBufferResult();
     if (!resultImage)
         return false;
 

Modified: trunk/Source/WebCore/platform/graphics/filters/FEFlood.h (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEFlood.h	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEFlood.h	2021-11-23 09:22:16 UTC (rev 286129)
@@ -39,8 +39,7 @@
 #if !USE(CG)
     // feFlood does not perform color interpolation of any kind, so the result is always in the current
     // color space regardless of the value of color-interpolation-filters.
-    void setOperatingColorSpace(const DestinationColorSpace&) override { FilterEffect::setResultColorSpace(DestinationColorSpace::SRGB()); }
-    void setResultColorSpace(const DestinationColorSpace&) override { FilterEffect::setResultColorSpace(DestinationColorSpace::SRGB()); }
+    void setOperatingColorSpace(const DestinationColorSpace&) override { }
 #endif
 
 private:

Modified: trunk/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -523,16 +523,14 @@
 {
     FilterEffect* in = inputEffect(0);
 
-    auto& destinationPixelBuffer = createPremultipliedImageResult();
+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
     if (!destinationPixelBuffer)
         return false;
 
-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
     setIsAlphaImage(in->isAlphaImage());
 
     IntRect effectDrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
-    in->copyPremultipliedResult(destinationPixelArray, effectDrawingRect, operatingColorSpace());
+    in->copyPixelBufferResult(*destinationPixelBuffer, effectDrawingRect);
     if (!m_stdX && !m_stdY)
         return true;
 
@@ -543,6 +541,7 @@
     if (!tmpImageData)
         return false;
 
+    auto& destinationPixelArray = destinationPixelBuffer->data();
     platformApply(destinationPixelArray, *tmpImageData, kernelSize.width(), kernelSize.height(), paintSize);
     return true;
 }

Modified: trunk/Source/WebCore/platform/graphics/filters/FELighting.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FELighting.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FELighting.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -475,16 +475,15 @@
 {
     FilterEffect* in = inputEffect(0);
 
-    auto& destinationPixelBuffer = createPremultipliedImageResult();
+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
     if (!destinationPixelBuffer)
         return false;
 
-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
     setIsAlphaImage(false);
 
     IntRect effectDrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
-    in->copyPremultipliedResult(destinationPixelArray, effectDrawingRect, operatingColorSpace());
+    in->copyPixelBufferResult(*destinationPixelBuffer, effectDrawingRect);
+
     // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3
     // standard has no test case for them, and other browsers (like Firefox) has strange
     // output for various kernelUnitLengths, and I am not sure they are reliable.
@@ -491,6 +490,8 @@
     // Anyway, feConvolveMatrix should also use the implementation
 
     IntSize absolutePaintSize = absolutePaintRect().size();
+    auto& destinationPixelArray = destinationPixelBuffer->data();
+
     drawLighting(destinationPixelArray, absolutePaintSize.width(), absolutePaintSize.height());
     return true;
 }

Modified: trunk/Source/WebCore/platform/graphics/filters/FEMerge.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEMerge.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEMerge.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -43,7 +43,7 @@
     unsigned size = numberOfEffectInputs();
     ASSERT(size > 0);
 
-    ImageBuffer* resultImage = createImageBufferResult();
+    auto resultImage = imageBufferResult();
     if (!resultImage)
         return false;
 
@@ -50,7 +50,7 @@
     GraphicsContext& filterContext = resultImage->context();
     for (unsigned i = 0; i < size; ++i) {
         FilterEffect* in = inputEffect(i);
-        if (ImageBuffer* inBuffer = in->imageBufferResult())
+        if (auto inBuffer = in->imageBufferResult())
             filterContext.drawImageBuffer(*inBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
     }
 

Modified: trunk/Source/WebCore/platform/graphics/filters/FEMorphology.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEMorphology.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEMorphology.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -229,14 +229,9 @@
     platformApplyGeneric(paintingData, 0, paintingData.height);
 }
 
-bool FEMorphology::platformApplyDegenerate(Uint8ClampedArray& dstPixelArray, const IntRect& imageRect, int radiusX, int radiusY)
+bool FEMorphology::isDegenerate(int radiusX, int radiusY) const
 {
-    if (radiusX < 0 || radiusY < 0 || (!radiusX && !radiusY)) {
-        FilterEffect* in = inputEffect(0);
-        in->copyPremultipliedResult(dstPixelArray, imageRect, operatingColorSpace());
-        return true;
-    }
-    return false;
+    return radiusX < 0 || radiusY < 0 || (!radiusX && !radiusY);
 }
 
 bool FEMorphology::platformApplySoftware(const Filter& filter)
@@ -243,33 +238,38 @@
 {
     FilterEffect* in = inputEffect(0);
 
-    auto& destinationPixelBuffer = createPremultipliedImageResult();
+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
     if (!destinationPixelBuffer)
         return false;
 
-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
     setIsAlphaImage(in->isAlphaImage());
 
     IntRect effectDrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
 
     IntSize radius = flooredIntSize(FloatSize(m_radiusX, m_radiusY));
-    if (platformApplyDegenerate(destinationPixelArray, effectDrawingRect, radius.width(), radius.height()))
+    if (isDegenerate(radius.width(), radius.height())) {
+        in->copyPixelBufferResult(*destinationPixelBuffer, effectDrawingRect);
         return true;
+    }
 
-    auto sourcePixelArray = in->premultipliedResult(effectDrawingRect, operatingColorSpace());
-    if (!sourcePixelArray)
-        return false;
-
     radius = flooredIntSize(filter.scaledByFilterScale({ m_radiusX, m_radiusY }));
     int radiusX = std::min(effectDrawingRect.width() - 1, radius.width());
     int radiusY = std::min(effectDrawingRect.height() - 1, radius.height());
 
-    if (platformApplyDegenerate(destinationPixelArray, effectDrawingRect, radiusX, radiusY))
+    if (isDegenerate(radiusX, radiusY)) {
+        in->copyPixelBufferResult(*destinationPixelBuffer, effectDrawingRect);
         return true;
-    
+    }
+
+    auto sourcePixelBuffer = in->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectDrawingRect, operatingColorSpace());
+    if (!sourcePixelBuffer)
+        return false;
+
+    auto& sourcePixelArray = sourcePixelBuffer->data();
+    auto& destinationPixelArray = destinationPixelBuffer->data();
+
     PaintingData paintingData;
-    paintingData.srcPixelArray = sourcePixelArray.get();
+    paintingData.srcPixelArray = &sourcePixelArray;
     paintingData.dstPixelArray = &destinationPixelArray;
     paintingData.width = ceilf(effectDrawingRect.width());
     paintingData.height = ceilf(effectDrawingRect.height());

Modified: trunk/Source/WebCore/platform/graphics/filters/FEMorphology.h (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEMorphology.h	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEMorphology.h	2021-11-23 09:22:16 UTC (rev 286129)
@@ -53,7 +53,7 @@
 
     WTF::TextStream& externalRepresentation(WTF::TextStream&, RepresentationType) const override;
 
-    bool platformApplyDegenerate(Uint8ClampedArray& dstPixelArray, const IntRect& imageRect, int radiusX, int radiusY);
+    bool isDegenerate(int radiusX, int radiusY) const;
 
     struct PaintingData {
         const Uint8ClampedArray* srcPixelArray;

Modified: trunk/Source/WebCore/platform/graphics/filters/FEOffset.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FEOffset.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FEOffset.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -68,8 +68,8 @@
 {
     FilterEffect* in = inputEffect(0);
 
-    ImageBuffer* resultImage = createImageBufferResult();
-    ImageBuffer* inBuffer = in->imageBufferResult();
+    auto resultImage = imageBufferResult();
+    auto inBuffer = in->imageBufferResult();
     if (!resultImage || !inBuffer)
         return false;
 

Modified: trunk/Source/WebCore/platform/graphics/filters/FETile.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FETile.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FETile.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -44,11 +44,10 @@
 
 bool FETile::platformApplySoftware(const Filter& filter)
 {
-// FIXME: See bug 47315. This is a hack to work around a compile failure, but is incorrect behavior otherwise.
     FilterEffect* in = inputEffect(0);
 
-    ImageBuffer* resultImage = createImageBufferResult();
-    ImageBuffer* inBuffer = in->imageBufferResult();
+    auto resultImage = imageBufferResult();
+    auto inBuffer = in->imageBufferResult();
     if (!resultImage || !inBuffer)
         return false;
 

Modified: trunk/Source/WebCore/platform/graphics/filters/FETurbulence.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FETurbulence.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FETurbulence.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -395,7 +395,7 @@
 
 bool FETurbulence::platformApplySoftware(const Filter& filter)
 {
-    auto& destinationPixelBuffer = createUnmultipliedImageResult();
+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Unpremultiplied);
     if (!destinationPixelBuffer)
         return false;
 

Modified: trunk/Source/WebCore/platform/graphics/filters/FilterEffect.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FilterEffect.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FilterEffect.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -27,29 +27,13 @@
 #include "Color.h"
 #include "Filter.h"
 #include "GeometryUtilities.h"
-#include "GraphicsContext.h"
 #include "ImageBuffer.h"
 #include "Logging.h"
 #include "PixelBuffer.h"
-#include <_javascript_Core/JSCInlines.h>
-#include <_javascript_Core/TypedArrayInlines.h>
 #include <wtf/text/TextStream.h>
 
-#if HAVE(ARM_NEON_INTRINSICS)
-#include <arm_neon.h>
-#endif
-
-#if USE(ACCELERATE)
-#include <Accelerate/Accelerate.h>
-#endif
-
 namespace WebCore {
 
-FilterEffect::FilterEffect(FilterEffect::Type type)
-    : FilterFunction(type)
-{
-}
-
 void FilterEffect::determineAbsolutePaintRect(const Filter&)
 {
     m_absolutePaintRect = IntRect();
@@ -157,7 +141,6 @@
     }
 
     determineAbsolutePaintRect(filter);
-    setResultColorSpace(m_operatingColorSpace);
 
     LOG_WITH_STREAM(Filters, stream
         << "FilterEffect " << filterName() << " " << this << " apply():"
@@ -171,69 +154,27 @@
     if (m_absolutePaintRect.isEmpty() || ImageBuffer::sizeNeedsClamping(m_absolutePaintRect.size()))
         return false;
 
-    if (requiresValidPreMultipliedPixels()) {
-        for (unsigned i = 0; i < size; ++i)
-            inputEffect(i)->correctFilterResultIfNeeded();
+    if (!mayProduceInvalidPremultipliedPixels()) {
+        for (auto& in : m_inputEffects)
+            in->correctPremultipliedResultIfNeeded();
     }
+    
+    if (!createResult())
+        return false;
 
     // Add platform specific apply functions here and return earlier.
     return platformApplySoftware(filter);
 }
 
-void FilterEffect::forceValidPreMultipliedPixels()
+bool FilterEffect::createResult()
 {
-    // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
-    if (!m_premultipliedImageResult)
-        return;
-
-    auto& imageArray = m_premultipliedImageResult->data();
-    uint8_t* pixelData = imageArray.data();
-    int pixelArrayLength = imageArray.length();
-
-    // We must have four bytes per pixel, and complete pixels
-    ASSERT(!(pixelArrayLength % 4));
-
-#if HAVE(ARM_NEON_INTRINSICS)
-    if (pixelArrayLength >= 64) {
-        uint8_t* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
-        do {
-            // Increments pixelData by 64.
-            uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
-            sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
-            sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
-            sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
-            vst4q_u8(pixelData, sixteenPixels);
-            pixelData += 64;
-        } while (pixelData < lastPixel);
-
-        pixelArrayLength &= 0x3f;
-        if (!pixelArrayLength)
-            return;
-    }
-#endif
-
-    int numPixels = pixelArrayLength / 4;
-
-    // Iterate over each pixel, checking alpha and adjusting color components if necessary
-    while (--numPixels >= 0) {
-        // Alpha is the 4th byte in a pixel
-        uint8_t a = *(pixelData + 3);
-        // Clamp each component to alpha, and increment the pixel location
-        for (int i = 0; i < 3; ++i) {
-            if (*pixelData > a)
-                *pixelData = a;
-            ++pixelData;
-        }
-        // Increment for alpha
-        ++pixelData;
-    }
+    m_filterImage = FilterImage::create(m_absolutePaintRect, RenderingMode::Unaccelerated, resultColorSpace());
+    return m_filterImage;
 }
 
 void FilterEffect::clearResult()
 {
-    m_imageBufferResult = nullptr;
-    m_unmultipliedImageResult = std::nullopt;
-    m_premultipliedImageResult = std::nullopt;
+    m_filterImage = nullptr;
 }
 
 void FilterEffect::clearResultsRecursive()
@@ -240,399 +181,49 @@
 {
     // Clear all results, regardless that the current effect has
     // a result. Can be used if an effect is in an erroneous state.
-    if (hasResult())
-        clearResult();
-
-    unsigned size = m_inputEffects.size();
-    for (unsigned i = 0; i < size; ++i)
-        m_inputEffects.at(i).get()->clearResultsRecursive();
+    clearResult();
+    for (auto& effect : m_inputEffects)
+        effect->clearResultsRecursive();
 }
 
 ImageBuffer* FilterEffect::imageBufferResult()
 {
-    LOG_WITH_STREAM(Filters, stream << "FilterEffect " << filterName() << " " << this << " imageBufferResult(). Existing image buffer " << m_imageBufferResult.get() <<  " m_premultipliedImageResult " << m_premultipliedImageResult << " m_unmultipliedImageResult " << m_unmultipliedImageResult);
-
     if (!hasResult())
         return nullptr;
-
-    if (m_imageBufferResult)
-        return m_imageBufferResult.get();
-
-    // FIXME: Respect the Filter::renderingMode() when creating the filter ImageBuffer result.
-    // For now just pass RenderingMode::Unaccelerated. This will not be a behavior change since
-    // this is what we do for the software filter code path anyway.
-    m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), RenderingMode::Unaccelerated, 1, m_resultColorSpace, PixelFormat::BGRA8);
-    if (!m_imageBufferResult)
-        return nullptr;
-
-    IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
-    if (m_premultipliedImageResult)
-        m_imageBufferResult->putPixelBuffer(*m_premultipliedImageResult, destinationRect);
-    else
-        m_imageBufferResult->putPixelBuffer(*m_unmultipliedImageResult, destinationRect);
-    return m_imageBufferResult.get();
+    return m_filterImage->imageBuffer();
 }
 
-RefPtr<Uint8ClampedArray> FilterEffect::unmultipliedResult(const IntRect& rect, std::optional<DestinationColorSpace> colorSpace)
+PixelBuffer* FilterEffect::pixelBufferResult(AlphaPremultiplication alphaFormat)
 {
-    IntSize scaledSize(rect.size());
-    ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
-    auto checkedArea = scaledSize.area<RecordOverflow>() * 4;
-    if (checkedArea.hasOverflowed())
+    if (!hasResult())
         return nullptr;
-    auto pixelArray = Uint8ClampedArray::tryCreateUninitialized(checkedArea);
-    if (!pixelArray)
-        return nullptr;
-    copyUnmultipliedResult(*pixelArray, rect, colorSpace);
-    return pixelArray;
+    return m_filterImage->pixelBuffer(alphaFormat);
 }
 
-RefPtr<Uint8ClampedArray> FilterEffect::premultipliedResult(const IntRect& rect, std::optional<DestinationColorSpace> colorSpace)
+std::optional<PixelBuffer> FilterEffect::getPixelBufferResult(AlphaPremultiplication alphaFormat, const IntRect& sourceRect, std::optional<DestinationColorSpace> colorSpace)
 {
-    IntSize scaledSize(rect.size());
-    ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
-    auto checkedArea = scaledSize.area<RecordOverflow>() * 4;
-    if (checkedArea.hasOverflowed())
-        return nullptr;
-    auto pixelArray = Uint8ClampedArray::tryCreateUninitialized(checkedArea);
-    if (!pixelArray)
-        return nullptr;
-    copyPremultipliedResult(*pixelArray, rect, colorSpace);
-    return pixelArray;
+    ASSERT(hasResult());
+    return m_filterImage->getPixelBuffer(alphaFormat, sourceRect, colorSpace);
 }
 
-void FilterEffect::copyImageBytes(const Uint8ClampedArray& source, Uint8ClampedArray& destination, const IntRect& rect) const
+void FilterEffect::copyPixelBufferResult(PixelBuffer& destinationPixelBuffer, const IntRect& sourceRect) const
 {
-    IntRect scaledRect(rect);
-    IntSize scaledPaintSize(m_absolutePaintRect.size());
-
-    // Initialize the destination to transparent black, if not entirely covered by the source.
-    if (scaledRect.x() < 0 || scaledRect.y() < 0 || scaledRect.maxX() > scaledPaintSize.width() || scaledRect.maxY() > scaledPaintSize.height())
-        memset(destination.data(), 0, destination.length());
-
-    // Early return if the rect does not intersect with the source.
-    if (scaledRect.maxX() <= 0 || scaledRect.maxY() <= 0 || scaledRect.x() >= scaledPaintSize.width() || scaledRect.y() >= scaledPaintSize.height())
-        return;
-
-    int xOrigin = scaledRect.x();
-    int xDest = 0;
-    if (xOrigin < 0) {
-        xDest = -xOrigin;
-        xOrigin = 0;
-    }
-    int xEnd = scaledRect.maxX();
-    if (xEnd > scaledPaintSize.width())
-        xEnd = scaledPaintSize.width();
-
-    int yOrigin = scaledRect.y();
-    int yDest = 0;
-    if (yOrigin < 0) {
-        yDest = -yOrigin;
-        yOrigin = 0;
-    }
-    int yEnd = scaledRect.maxY();
-    if (yEnd > scaledPaintSize.height())
-        yEnd = scaledPaintSize.height();
-
-    int size = (xEnd - xOrigin) * 4;
-    int destinationScanline = scaledRect.width() * 4;
-    int sourceScanline = scaledPaintSize.width() * 4;
-    uint8_t* destinationPixel = destination.data() + ((yDest * scaledRect.width()) + xDest) * 4;
-    const uint8_t* sourcePixel = source.data() + ((yOrigin * scaledPaintSize.width()) + xOrigin) * 4;
-
-    while (yOrigin < yEnd) {
-        memcpy(destinationPixel, sourcePixel, size);
-        destinationPixel += destinationScanline;
-        sourcePixel += sourceScanline;
-        ++yOrigin;
-    }
-}
-
-static void copyPremultiplyingAlpha(const Uint8ClampedArray& source, Uint8ClampedArray& destination, const IntSize& inputSize)
-{
-#if USE(ACCELERATE)
-    size_t rowBytes = inputSize.width() * 4;
-
-    vImage_Buffer src;
-    src.width = inputSize.width();
-    src.height = inputSize.height();
-    src.rowBytes = rowBytes;
-    src.data = ""
-
-    vImage_Buffer dest;
-    dest.width = inputSize.width();
-    dest.height = inputSize.height();
-    dest.rowBytes = rowBytes;
-    dest.data = ""
-
-    vImagePremultiplyData_RGBA8888(&src, &dest, kvImageNoFlags);
-#else
-    const uint8_t* sourceComponent = source.data();
-    const uint8_t* end = sourceComponent + (inputSize.area() * 4).value();
-    uint8_t* destinationComponent = destination.data();
-
-    while (sourceComponent < end) {
-        int alpha = sourceComponent[3];
-        destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
-        destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
-        destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
-        destinationComponent[3] = alpha;
-        sourceComponent += 4;
-        destinationComponent += 4;
-    }
-#endif
-}
-
-static void copyUnpremultiplyingAlpha(const Uint8ClampedArray& source, Uint8ClampedArray& destination, const IntSize& inputSize)
-{
-#if USE(ACCELERATE)
-    size_t rowBytes = inputSize.width() * 4;
-
-    vImage_Buffer src;
-    src.width = inputSize.width();
-    src.height = inputSize.height();
-    src.rowBytes = rowBytes;
-    src.data = ""
-
-    vImage_Buffer dest;
-    dest.width = inputSize.width();
-    dest.height = inputSize.height();
-    dest.rowBytes = rowBytes;
-    dest.data = ""
-
-    vImageUnpremultiplyData_RGBA8888(&src, &dest, kvImageNoFlags);
-#else
-    const uint8_t* sourceComponent = source.data();
-    const uint8_t* end = sourceComponent + (inputSize.area() * 4).value();
-    uint8_t* destinationComponent = destination.data();
-    while (sourceComponent < end) {
-        int alpha = sourceComponent[3];
-        if (alpha) {
-            destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
-            destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
-            destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
-        } else {
-            destinationComponent[0] = 0;
-            destinationComponent[1] = 0;
-            destinationComponent[2] = 0;
-        }
-        destinationComponent[3] = alpha;
-        sourceComponent += 4;
-        destinationComponent += 4;
-    }
-#endif
-}
-
-std::optional<PixelBuffer> FilterEffect::convertPixelBufferToColorSpace(const DestinationColorSpace& targetColorSpace, PixelBuffer& pixelBuffer)
-{
-    // FIXME: Using an ImageBuffer to perform the color space conversion is unnecessary. We can do it directly.
-
-    IntRect destinationRect(IntPoint(), pixelBuffer.size());
-    FloatSize clampedSize = ImageBuffer::clampedSize(destinationRect.size());
-    // Create an ImageBuffer to store incoming PixelBuffer
-    auto buffer = ImageBuffer::create(clampedSize, RenderingMode::Unaccelerated, 1, operatingColorSpace(), PixelFormat::BGRA8);
-    if (!buffer)
-        return std::nullopt;
-    buffer->putPixelBuffer(pixelBuffer, destinationRect);
-    return convertImageBufferToColorSpace(targetColorSpace, *buffer, destinationRect, pixelBuffer.format().alphaFormat);
-}
-
-std::optional<PixelBuffer> FilterEffect::convertImageBufferToColorSpace(const DestinationColorSpace& targetColorSpace, ImageBuffer& inputBuffer, const IntRect& rect, AlphaPremultiplication outputAlphaFormat)
-{
-    // FIXME: This can be done more directly using PixelBufferConversion.
-
-    FloatSize clampedSize = ImageBuffer::clampedSize(rect.size());
-
-    // Create an ImageBuffer with the correct color space and utilize CG to handle color space conversion
-    auto convertedBuffer = ImageBuffer::create(clampedSize, RenderingMode::Unaccelerated, 1, targetColorSpace, PixelFormat::BGRA8);
-    if (!convertedBuffer)
-        return std::nullopt;
-
-    // Color space conversion happens internally when drawing from one image buffer to another
-    convertedBuffer->context().drawImageBuffer(inputBuffer, rect);
-    
-    PixelBufferFormat format { outputAlphaFormat, PixelFormat::RGBA8, targetColorSpace };
-    return convertedBuffer->getPixelBuffer(format, rect);
-}
-
-void FilterEffect::copyConvertedImageBufferToDestination(Uint8ClampedArray& destination, const DestinationColorSpace& colorSpace, AlphaPremultiplication outputFormat, const IntRect& destRect)
-{
-    // Converts the data stored in m_imageBufferResult, and save to destination
-    auto convertedPixelBuffer = convertImageBufferToColorSpace(colorSpace, *m_imageBufferResult, { IntPoint(), m_absolutePaintRect.size() }, outputFormat);
-    if (!convertedPixelBuffer)
-        return;
-    copyImageBytes(convertedPixelBuffer->data(), destination, destRect);
-}
-
-void FilterEffect::copyConvertedPixelBufferToDestination(Uint8ClampedArray& destination, PixelBuffer& pixelBuffer, const DestinationColorSpace& colorSpace, const IntRect& destRect)
-{
-    // Converts the data stored in m_unmultipliedImageResult/m_premultipliedImageResult,
-    // whichever isn't null, and save to destination
-    auto convertedPixelBuffer = convertPixelBufferToColorSpace(colorSpace, pixelBuffer);
-    if (!convertedPixelBuffer)
-        return;
-    copyImageBytes(convertedPixelBuffer->data(), destination, destRect);
-}
-
-void FilterEffect::copyUnmultipliedResult(Uint8ClampedArray& destination, const IntRect& rect, std::optional<DestinationColorSpace> colorSpace)
-{
     ASSERT(hasResult());
-    
-    LOG_WITH_STREAM(Filters, stream << "FilterEffect " << filterName() << " " << this << " copyUnmultipliedResult(). Existing image buffer " << m_imageBufferResult.get() <<  " m_premultipliedImageResult " << m_premultipliedImageResult << " m_unmultipliedImageResult " << m_unmultipliedImageResult);
-
-    if (!m_unmultipliedImageResult) {
-        // We prefer a conversion from the image buffer.
-        if (m_imageBufferResult) {
-            if (requiresPixelBufferColorSpaceConversion(colorSpace)) {
-                copyConvertedImageBufferToDestination(destination, *colorSpace, AlphaPremultiplication::Unpremultiplied, rect);
-                return;
-            }
-
-            ASSERT(m_imageBufferResult->colorSpace() == m_resultColorSpace);
-            PixelBufferFormat format { AlphaPremultiplication::Unpremultiplied, PixelFormat::RGBA8, m_resultColorSpace };
-            m_unmultipliedImageResult = m_imageBufferResult->getPixelBuffer(format, { IntPoint(), m_absolutePaintRect.size() });
-            if (!m_unmultipliedImageResult)
-                return;
-        } else {
-            IntSize inputSize(m_absolutePaintRect.size());
-            ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
-            
-            ASSERT(m_premultipliedImageResult->format().colorSpace == m_resultColorSpace);
-            PixelBufferFormat format { AlphaPremultiplication::Unpremultiplied, PixelFormat::RGBA8, m_resultColorSpace };
-            m_unmultipliedImageResult = PixelBuffer::tryCreate(format, inputSize);
-            if (!m_unmultipliedImageResult)
-                return;
-            copyUnpremultiplyingAlpha(m_premultipliedImageResult->data(), m_unmultipliedImageResult->data(), inputSize);
-        }
-    }
-    if (requiresPixelBufferColorSpaceConversion(colorSpace)) {
-        copyConvertedPixelBufferToDestination(destination, *m_unmultipliedImageResult, *colorSpace, rect);
-        return;
-    }
-    copyImageBytes(m_unmultipliedImageResult->data(), destination, rect);
+    m_filterImage->copyPixelBuffer(destinationPixelBuffer, sourceRect);
 }
 
-void FilterEffect::copyPremultipliedResult(Uint8ClampedArray& destination, const IntRect& rect, std::optional<DestinationColorSpace> colorSpace)
+void FilterEffect::correctPremultipliedResultIfNeeded()
 {
-    ASSERT(hasResult());
-
-    LOG_WITH_STREAM(Filters, stream << "FilterEffect " << filterName() << " " << this << " copyPremultipliedResult(). Existing image buffer " << m_imageBufferResult.get() <<  " m_premultipliedImageResult " << m_premultipliedImageResult << " m_unmultipliedImageResult " << m_unmultipliedImageResult);
-
-    if (!m_premultipliedImageResult) {
-        // We prefer a conversion from the image buffer.
-        if (m_imageBufferResult) {
-            if (requiresPixelBufferColorSpaceConversion(colorSpace)) {
-                copyConvertedImageBufferToDestination(destination, *colorSpace, AlphaPremultiplication::Premultiplied, rect);
-                return;
-            }
-
-            ASSERT(m_imageBufferResult->colorSpace() == m_resultColorSpace);
-            PixelBufferFormat format { AlphaPremultiplication::Premultiplied, PixelFormat::RGBA8, m_resultColorSpace };
-            m_premultipliedImageResult = m_imageBufferResult->getPixelBuffer(format, { IntPoint(), m_absolutePaintRect.size() });
-            if (!m_premultipliedImageResult)
-                return;
-        } else {
-            IntSize inputSize(m_absolutePaintRect.size());
-            ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize));
-
-            ASSERT(m_unmultipliedImageResult->format().colorSpace == m_resultColorSpace);
-            PixelBufferFormat format { AlphaPremultiplication::Premultiplied, PixelFormat::RGBA8, m_resultColorSpace };
-            m_premultipliedImageResult = PixelBuffer::tryCreate(format, inputSize);
-            if (!m_premultipliedImageResult)
-                return;
-            copyPremultiplyingAlpha(m_unmultipliedImageResult->data(), m_premultipliedImageResult->data(), inputSize);
-        }
-    }
-
-    if (requiresPixelBufferColorSpaceConversion(colorSpace)) {
-        copyConvertedPixelBufferToDestination(destination, *m_premultipliedImageResult, *colorSpace, rect);
+    if (!hasResult() || !mayProduceInvalidPremultipliedPixels())
         return;
-    }
-    copyImageBytes(m_premultipliedImageResult->data(), destination, rect);
+    m_filterImage->correctPremultipliedPixelBuffer();
 }
 
-ImageBuffer* FilterEffect::createImageBufferResult()
-{
-    LOG(Filters, "FilterEffect %s %p createImageBufferResult %dx%d", filterName().characters8(), this, m_absolutePaintRect.size().width(), m_absolutePaintRect.size().height());
-
-    // Only one result type is allowed.
-    ASSERT(!hasResult());
-    if (m_absolutePaintRect.isEmpty())
-        return nullptr;
-
-    FloatSize clampedSize = ImageBuffer::clampedSize(m_absolutePaintRect.size());
-    m_imageBufferResult = ImageBuffer::create(clampedSize, RenderingMode::Unaccelerated, 1, m_resultColorSpace, PixelFormat::BGRA8);
-    return m_imageBufferResult.get();
-}
-
-std::optional<PixelBuffer>& FilterEffect::createUnmultipliedImageResult()
-{
-    LOG(Filters, "FilterEffect %s %p createUnmultipliedImageResult", filterName().characters8(), this);
-
-    // Only one result type is allowed.
-    ASSERT(!hasResult());
-    ASSERT(!m_unmultipliedImageResult);
-
-    if (m_absolutePaintRect.isEmpty())
-        return m_unmultipliedImageResult;
-
-    IntSize resultSize(m_absolutePaintRect.size());
-    ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize));
-    PixelBufferFormat format { AlphaPremultiplication::Unpremultiplied, PixelFormat::RGBA8, m_resultColorSpace };
-    m_unmultipliedImageResult = PixelBuffer::tryCreate(format, resultSize);
-    return m_unmultipliedImageResult;
-}
-
-std::optional<PixelBuffer>& FilterEffect::createPremultipliedImageResult()
-{
-    LOG(Filters, "FilterEffect %s %p createPremultipliedImageResult", filterName().characters8(), this);
-
-    // Only one result type is allowed.
-    ASSERT(!hasResult());
-    ASSERT(!m_premultipliedImageResult);
-
-    if (m_absolutePaintRect.isEmpty())
-        return m_premultipliedImageResult;
-
-    IntSize resultSize(m_absolutePaintRect.size());
-    ASSERT(!ImageBuffer::sizeNeedsClamping(resultSize));
-    PixelBufferFormat format { AlphaPremultiplication::Premultiplied, PixelFormat::RGBA8, m_resultColorSpace };
-    m_premultipliedImageResult = PixelBuffer::tryCreate(format, resultSize);
-    return m_premultipliedImageResult;
-}
-
-bool FilterEffect::requiresPixelBufferColorSpaceConversion(std::optional<DestinationColorSpace> destinationColorSpace)
-{
-#if USE(CG)
-    // This function determines whether we need the step of an extra color space conversion
-    // We only need extra color conversion when 1) color space is different in the input
-    // AND 2) the filter is manipulating raw pixels
-    return destinationColorSpace && resultColorSpace() != *destinationColorSpace;
-#else
-    // Additional color space conversion is not needed on non-CG
-    UNUSED_PARAM(destinationColorSpace);
-    return false;
-#endif
-}
-
 void FilterEffect::transformResultColorSpace(const DestinationColorSpace& destinationColorSpace)
 {
-#if USE(CG)
-    // CG handles color space adjustments internally.
-    UNUSED_PARAM(destinationColorSpace);
-#else
-    if (!hasResult() || destinationColorSpace == m_resultColorSpace)
+    if (!hasResult())
         return;
-
-    // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
-    // color space transform support for the {pre,un}multiplied arrays.
-    imageBufferResult()->transformToColorSpace(destinationColorSpace);
-
-    m_resultColorSpace = destinationColorSpace;
-
-    m_unmultipliedImageResult = std::nullopt;
-    m_premultipliedImageResult = std::nullopt;
-#endif
+    m_filterImage->transformToColorSpace(destinationColorSpace);
 }
 
 TextStream& FilterEffect::externalRepresentation(TextStream& ts, RepresentationType representationType) const
@@ -643,8 +234,8 @@
     if (representationType == RepresentationType::Debugging) {
         TextStream::IndentScope indentScope(ts);
         ts.dumpProperty("alpha image", m_alphaImage);
-        ts.dumpProperty("operating colorspace", m_operatingColorSpace);
-        ts.dumpProperty("result colorspace", m_resultColorSpace);
+        ts.dumpProperty("operating colorspace", operatingColorSpace());
+        ts.dumpProperty("result colorspace", resultColorSpace());
         ts << "\n" << indent;
     }
     return ts;

Modified: trunk/Source/WebCore/platform/graphics/filters/FilterEffect.h (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FilterEffect.h	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/FilterEffect.h	2021-11-23 09:22:16 UTC (rev 286129)
@@ -22,17 +22,11 @@
 
 #pragma once
 
-#include "AlphaPremultiplication.h"
 #include "DestinationColorSpace.h"
 #include "FilterEffectVector.h"
 #include "FilterFunction.h"
-#include "FloatRect.h"
+#include "FilterImage.h"
 #include "IntRect.h"
-#include "IntRectExtent.h"
-#include "PixelBuffer.h"
-#include <_javascript_Core/Forward.h>
-#include <wtf/MathExtras.h>
-#include <wtf/RefPtr.h>
 #include <wtf/Vector.h>
 
 namespace WTF {
@@ -46,26 +40,22 @@
 
 class FilterEffect : public FilterFunction {
 public:
+    bool createResult();
     void clearResult() override;
     void clearResultsRecursive();
 
+    bool hasResult() const { return m_filterImage; }
+
     ImageBuffer* imageBufferResult();
-    RefPtr<Uint8ClampedArray> unmultipliedResult(const IntRect&, std::optional<DestinationColorSpace> = std::nullopt);
-    RefPtr<Uint8ClampedArray> premultipliedResult(const IntRect&, std::optional<DestinationColorSpace> = std::nullopt);
-    void copyUnmultipliedResult(Uint8ClampedArray& destination, const IntRect&, std::optional<DestinationColorSpace> = std::nullopt);
-    void copyPremultipliedResult(Uint8ClampedArray& destination, const IntRect&, std::optional<DestinationColorSpace> = std::nullopt);
+    PixelBuffer* pixelBufferResult(AlphaPremultiplication);
+    std::optional<PixelBuffer> getPixelBufferResult(AlphaPremultiplication, const IntRect& sourceRect, std::optional<DestinationColorSpace> = std::nullopt);
+    void copyPixelBufferResult(PixelBuffer& destinationPixelBuffer, const IntRect& sourceRect) const;
+    void correctPremultipliedResultIfNeeded();
+
     FilterEffectVector& inputEffects() { return m_inputEffects; }
     FilterEffect* inputEffect(unsigned) const;
     unsigned numberOfEffectInputs() const { return m_inputEffects.size(); }
     
-    inline bool hasResult() const
-    {
-        // This function needs platform specific checks, if the memory managment is not done by FilterEffect.
-        return m_imageBufferResult
-            || m_unmultipliedImageResult
-            || m_premultipliedImageResult;
-    }
-
     FloatRect drawingRegionOfInputImage(const IntRect&) const;
     IntRect requestedRegionOfInputPixelBuffer(const IntRect&) const;
     
@@ -123,9 +113,9 @@
 
     const DestinationColorSpace& operatingColorSpace() const { return m_operatingColorSpace; }
     virtual void setOperatingColorSpace(const DestinationColorSpace& colorSpace) { m_operatingColorSpace = colorSpace; }
-    const DestinationColorSpace& resultColorSpace() const { return m_resultColorSpace; }
-    virtual void setResultColorSpace(const DestinationColorSpace& colorSpace) { m_resultColorSpace = colorSpace; }
 
+    virtual const DestinationColorSpace& resultColorSpace() const { return m_operatingColorSpace; }
+
     virtual void transformResultColorSpace(FilterEffect* in, const int) { in->transformResultColorSpace(m_operatingColorSpace); }
     void transformResultColorSpace(const DestinationColorSpace&);
     
@@ -138,36 +128,18 @@
     }
 
 protected:
-    FilterEffect(Type);
-    
-    ImageBuffer* createImageBufferResult();
-    std::optional<PixelBuffer>& createUnmultipliedImageResult();
-    std::optional<PixelBuffer>& createPremultipliedImageResult();
+    using FilterFunction::FilterFunction;
 
-    // Return true if the filter will only operate correctly on valid RGBA values, with
-    // alpha in [0,255] and each color component in [0, alpha].
-    virtual bool requiresValidPreMultipliedPixels() { return true; }
+    virtual bool mayProduceInvalidPremultipliedPixels() const { return false; }
 
-    // If a pre-multiplied image, check every pixel for validity and correct if necessary.
-    void forceValidPreMultipliedPixels();
-
     void clipAbsolutePaintRect();
 
 private:
     virtual bool platformApplySoftware(const Filter&) = 0;
 
-    void copyImageBytes(const Uint8ClampedArray& source, Uint8ClampedArray& destination, const IntRect&) const;
-    void copyConvertedImageBufferToDestination(Uint8ClampedArray&, const DestinationColorSpace&, AlphaPremultiplication, const IntRect&);
-    void copyConvertedPixelBufferToDestination(Uint8ClampedArray&, PixelBuffer&, const DestinationColorSpace&, const IntRect&);
-    bool requiresPixelBufferColorSpaceConversion(std::optional<DestinationColorSpace>);
-    std::optional<PixelBuffer> convertImageBufferToColorSpace(const DestinationColorSpace&, ImageBuffer&, const IntRect&, AlphaPremultiplication);
-    std::optional<PixelBuffer> convertPixelBufferToColorSpace(const DestinationColorSpace&, PixelBuffer&);
-    
     FilterEffectVector m_inputEffects;
 
-    RefPtr<ImageBuffer> m_imageBufferResult;
-    std::optional<PixelBuffer> m_unmultipliedImageResult;
-    std::optional<PixelBuffer> m_premultipliedImageResult;
+    RefPtr<FilterImage> m_filterImage;
 
     IntRect m_absolutePaintRect;
     
@@ -195,12 +167,7 @@
     // Should the effect clip to its primitive region, or expand to use the combined region of its inputs.
     bool m_clipsToBounds { true };
 
-#if ENABLE(DESTINATION_COLOR_SPACE_LINEAR_SRGB)
-    DestinationColorSpace m_operatingColorSpace { DestinationColorSpace::LinearSRGB() };
-#else
     DestinationColorSpace m_operatingColorSpace { DestinationColorSpace::SRGB() };
-#endif
-    DestinationColorSpace m_resultColorSpace { DestinationColorSpace::SRGB() };
 };
 
 WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, const FilterEffect&);

Added: trunk/Source/WebCore/platform/graphics/filters/FilterImage.cpp (0 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FilterImage.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/filters/FilterImage.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2021 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "FilterImage.h"
+
+#include "GraphicsContext.h"
+#include "ImageBuffer.h"
+#include "PixelBuffer.h"
+#include "PixelBufferConversion.h"
+
+#if HAVE(ARM_NEON_INTRINSICS)
+#include <arm_neon.h>
+#endif
+
+namespace WebCore {
+
+RefPtr<FilterImage> FilterImage::create(const IntRect& absoluteImageRect, RenderingMode renderingMode, const DestinationColorSpace& colorSpace)
+{
+    ASSERT(!ImageBuffer::sizeNeedsClamping(absoluteImageRect.size()));
+    return adoptRef(new FilterImage(absoluteImageRect, renderingMode, colorSpace));
+}
+
+FilterImage::FilterImage(const IntRect& absoluteImageRect, RenderingMode renderingMode, const DestinationColorSpace& colorSpace)
+    : m_absoluteImageRect(absoluteImageRect)
+    , m_renderingMode(renderingMode)
+    , m_colorSpace(colorSpace)
+{
+}
+
+ImageBuffer* FilterImage::imageBuffer()
+{
+    if (m_imageBuffer)
+        return m_imageBuffer.get();
+
+    m_imageBuffer = ImageBuffer::create(m_absoluteImageRect.size(), m_renderingMode, 1, m_colorSpace, PixelFormat::BGRA8);
+    if (!m_imageBuffer)
+        return nullptr;
+
+    auto imageBufferRect = IntRect { { }, m_absoluteImageRect.size() };
+
+    if (pixelBufferSlot(AlphaPremultiplication::Premultiplied))
+        m_imageBuffer->putPixelBuffer(*pixelBufferSlot(AlphaPremultiplication::Premultiplied), imageBufferRect);
+    else if (pixelBufferSlot(AlphaPremultiplication::Unpremultiplied))
+        m_imageBuffer->putPixelBuffer(*pixelBufferSlot(AlphaPremultiplication::Unpremultiplied), imageBufferRect);
+
+    return m_imageBuffer.get();
+}
+
+static void copyImageBytes(const PixelBuffer& sourcePixelBuffer, PixelBuffer& destinationPixelBuffer)
+{
+    ASSERT(sourcePixelBuffer.size() == destinationPixelBuffer.size());
+
+    auto destinationSize = destinationPixelBuffer.size();
+    unsigned rowBytes = destinationSize.width() * 4;
+
+    ConstPixelBufferConversionView source { sourcePixelBuffer.format(), rowBytes, sourcePixelBuffer.data().data() };
+    PixelBufferConversionView destination { destinationPixelBuffer.format(), rowBytes, destinationPixelBuffer.data().data() };
+
+    convertImagePixels(source, destination, destinationSize);
+}
+
+static void copyImageBytes(const PixelBuffer& sourcePixelBuffer, PixelBuffer& destinationPixelBuffer, const IntRect& sourceRect)
+{
+    auto& source = sourcePixelBuffer.data();
+    auto& destination = destinationPixelBuffer.data();
+
+    auto sourcePixelBufferRect = IntRect { { }, sourcePixelBuffer.size() };
+    auto destinationPixelBufferRect = IntRect { { }, destinationPixelBuffer.size() };
+
+    auto sourceRectClipped = intersection(sourcePixelBufferRect, sourceRect);
+    auto destinationRect = IntRect { { }, sourceRectClipped.size() };
+
+    if (sourceRect.x() < 0)
+        destinationRect.setX(-sourceRect.x());
+
+    if (sourceRect.y() < 0)
+        destinationRect.setY(-sourceRect.y());
+
+    destinationRect.intersect(destinationPixelBufferRect);
+    sourceRectClipped.setSize(destinationRect.size());
+
+    // Initialize the destination to transparent black, if not entirely covered by the source.
+    if (destinationRect.size() != destinationPixelBufferRect.size())
+        destination.zeroFill();
+
+    // Early return if the rect does not intersect with the source.
+    if (destinationRect.isEmpty())
+        return;
+
+    int size = sourceRectClipped.width() * 4;
+    int destinationBytesPerRow = destinationPixelBufferRect.width() * 4;
+    int sourceBytesPerRow = sourcePixelBufferRect.width() * 4;
+    uint8_t* destinationPixel = destination.data() + destinationRect.y() * destinationBytesPerRow + destinationRect.x() * 4;
+    const uint8_t* sourcePixel = source.data() + sourceRectClipped.y() * sourceBytesPerRow + sourceRectClipped.x() * 4;
+
+    for (int y = 0; y < sourceRectClipped.height(); ++y) {
+        memcpy(destinationPixel, sourcePixel, size);
+        destinationPixel += destinationBytesPerRow;
+        sourcePixel += sourceBytesPerRow;
+    }
+}
+
+static std::optional<PixelBuffer> getConvertedPixelBuffer(ImageBuffer& imageBuffer, AlphaPremultiplication alphaFormat, const IntRect& sourceRect, DestinationColorSpace colorSpace)
+{
+    auto clampedSize = ImageBuffer::clampedSize(sourceRect.size());
+    auto convertedImageBuffer = ImageBuffer::create(clampedSize, RenderingMode::Unaccelerated, 1, colorSpace, PixelFormat::BGRA8);
+    
+    if (!convertedImageBuffer)
+        return std::nullopt;
+
+    // Color space conversion happens internally when drawing from one image buffer to another
+    convertedImageBuffer->context().drawImageBuffer(imageBuffer, sourceRect);
+    PixelBufferFormat format { alphaFormat, PixelFormat::RGBA8, colorSpace };
+    return convertedImageBuffer->getPixelBuffer(format, sourceRect);
+}
+
+static std::optional<PixelBuffer> getConvertedPixelBuffer(PixelBuffer& sourcePixelBuffer, AlphaPremultiplication alphaFormat, DestinationColorSpace colorSpace)
+{
+    auto sourceRect = IntRect { { } , sourcePixelBuffer.size() };
+    auto clampedSize = ImageBuffer::clampedSize(sourceRect.size());
+
+    auto& sourceColorSpace = sourcePixelBuffer.format().colorSpace;
+    auto imageBuffer = ImageBuffer::create(clampedSize, RenderingMode::Unaccelerated, 1, sourceColorSpace, PixelFormat::BGRA8);
+    if (!imageBuffer)
+        return std::nullopt;
+
+    imageBuffer->putPixelBuffer(sourcePixelBuffer, sourceRect);
+    return getConvertedPixelBuffer(*imageBuffer, alphaFormat, sourceRect, colorSpace);
+}
+
+bool FilterImage::requiresPixelBufferColorSpaceConversion(std::optional<DestinationColorSpace> colorSpace) const
+{
+#if USE(CG)
+    // This function determines whether we need the step of an extra color space conversion
+    // We only need extra color conversion when 1) color space is different in the input
+    // AND 2) the filter is manipulating raw pixels
+    return colorSpace && m_colorSpace != *colorSpace;
+#else
+    // Additional color space conversion is not needed on non-CG
+    UNUSED_PARAM(colorSpace);
+    return false;
+#endif
+}
+
+std::optional<PixelBuffer>& FilterImage::pixelBufferSlot(AlphaPremultiplication alphaFormat)
+{
+    return alphaFormat == AlphaPremultiplication::Unpremultiplied ? m_unpremultipliedPixelBuffer : m_premultipliedPixelBuffer;
+}
+
+PixelBuffer* FilterImage::pixelBuffer(AlphaPremultiplication alphaFormat)
+{
+    auto& pixelBuffer = pixelBufferSlot(alphaFormat);
+    if (pixelBuffer)
+        return &pixelBuffer.value();
+
+    PixelBufferFormat format { alphaFormat, PixelFormat::RGBA8, m_colorSpace };
+
+    if (m_imageBuffer) {
+        pixelBuffer = m_imageBuffer->getPixelBuffer(format, { { }, m_absoluteImageRect.size() });
+        if (!pixelBuffer)
+            return nullptr;
+        return &pixelBuffer.value();
+    }
+
+    IntSize logicalSize(m_absoluteImageRect.size());
+    ASSERT(!ImageBuffer::sizeNeedsClamping(logicalSize));
+
+    pixelBuffer = PixelBuffer::tryCreate(format, logicalSize);
+    if (!pixelBuffer)
+        return nullptr;
+
+    if (alphaFormat == AlphaPremultiplication::Unpremultiplied) {
+        if (auto& sourcePixelBuffer = pixelBufferSlot(AlphaPremultiplication::Premultiplied))
+            copyImageBytes(*sourcePixelBuffer, *pixelBuffer);
+    } else {
+        if (auto& sourcePixelBuffer = pixelBufferSlot(AlphaPremultiplication::Unpremultiplied))
+            copyImageBytes(*sourcePixelBuffer, *pixelBuffer);
+    }
+
+    return &pixelBuffer.value();
+}
+
+std::optional<PixelBuffer> FilterImage::getPixelBuffer(AlphaPremultiplication alphaFormat, const IntRect& sourceRect, std::optional<DestinationColorSpace> colorSpace)
+{
+    ASSERT(!ImageBuffer::sizeNeedsClamping(sourceRect.size()));
+
+    PixelBufferFormat format { alphaFormat, PixelFormat::RGBA8, colorSpace? *colorSpace : m_colorSpace };
+
+    auto pixelBuffer = PixelBuffer::tryCreate(format, sourceRect.size());
+    if (!pixelBuffer)
+        return std::nullopt;
+
+    copyPixelBuffer(*pixelBuffer, sourceRect);
+    return pixelBuffer;
+}
+
+void FilterImage::copyPixelBuffer(PixelBuffer& destinationPixelBuffer, const IntRect& sourceRect)
+{
+    auto alphaFormat = destinationPixelBuffer.format().alphaFormat;
+    auto& colorSpace = destinationPixelBuffer.format().colorSpace;
+
+    auto* sourcePixelBuffer = pixelBufferSlot(alphaFormat) ? &pixelBufferSlot(alphaFormat).value() : nullptr;
+
+    if (!sourcePixelBuffer) {
+        if (requiresPixelBufferColorSpaceConversion(colorSpace)) {
+            // We prefer a conversion from the image buffer.
+            if (m_imageBuffer) {
+                IntRect rect { { }, m_absoluteImageRect.size() };
+                if (auto convertedPixelBuffer = getConvertedPixelBuffer(*m_imageBuffer, alphaFormat, rect, colorSpace))
+                    copyImageBytes(*convertedPixelBuffer, destinationPixelBuffer, sourceRect);
+                return;
+            }
+        }
+
+        sourcePixelBuffer = this->pixelBuffer(alphaFormat);
+    }
+
+    if (!sourcePixelBuffer)
+        return;
+
+    if (requiresPixelBufferColorSpaceConversion(colorSpace)) {
+        if (auto convertedPixelBuffer = getConvertedPixelBuffer(*sourcePixelBuffer, alphaFormat, colorSpace))
+            copyImageBytes(*convertedPixelBuffer, destinationPixelBuffer, sourceRect);
+        return;
+    }
+
+    copyImageBytes(*sourcePixelBuffer, destinationPixelBuffer, sourceRect);
+}
+
+void FilterImage::correctPremultipliedPixelBuffer()
+{
+    // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
+    if (!m_premultipliedPixelBuffer)
+        return;
+
+    Uint8ClampedArray& imageArray = m_premultipliedPixelBuffer->data();
+    uint8_t* pixelData = imageArray.data();
+    int pixelArrayLength = imageArray.length();
+
+    // We must have four bytes per pixel, and complete pixels
+    ASSERT(!(pixelArrayLength % 4));
+
+#if HAVE(ARM_NEON_INTRINSICS)
+    if (pixelArrayLength >= 64) {
+        uint8_t* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
+        do {
+            // Increments pixelData by 64.
+            uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
+            sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
+            sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
+            sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
+            vst4q_u8(pixelData, sixteenPixels);
+            pixelData += 64;
+        } while (pixelData < lastPixel);
+
+        pixelArrayLength &= 0x3f;
+        if (!pixelArrayLength)
+            return;
+    }
+#endif
+
+    int numPixels = pixelArrayLength / 4;
+
+    // Iterate over each pixel, checking alpha and adjusting color components if necessary
+    while (--numPixels >= 0) {
+        // Alpha is the 4th byte in a pixel
+        uint8_t a = *(pixelData + 3);
+        // Clamp each component to alpha, and increment the pixel location
+        for (int i = 0; i < 3; ++i) {
+            if (*pixelData > a)
+                *pixelData = a;
+            ++pixelData;
+        }
+        // Increment for alpha
+        ++pixelData;
+    }
+}
+
+void FilterImage::transformToColorSpace(const DestinationColorSpace& colorSpace)
+{
+#if USE(CG)
+    // CG handles color space adjustments internally.
+    UNUSED_PARAM(colorSpace);
+#else
+    if (colorSpace == m_colorSpace)
+        return;
+
+    // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
+    // color space transform support for the {pre,un}multiplied arrays.
+    if (auto imageBuffer = this->imageBuffer())
+        imageBuffer->transformToColorSpace(colorSpace);
+
+    m_colorSpace = colorSpace;
+    m_unpremultipliedPixelBuffer = std::nullopt;
+    m_premultipliedPixelBuffer = std::nullopt;
+#endif
+}
+
+} // namespace WebCore

Added: trunk/Source/WebCore/platform/graphics/filters/FilterImage.h (0 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/FilterImage.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/filters/FilterImage.h	2021-11-23 09:22:16 UTC (rev 286129)
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ImageBuffer.h"
+#include "PixelBuffer.h"
+#include <_javascript_Core/Forward.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class ImageBuffer;
+class PixelBuffer;
+
+class FilterImage : public RefCounted<FilterImage> {
+public:
+    static RefPtr<FilterImage> create(const IntRect& absoluteImageRect, RenderingMode, const DestinationColorSpace&);
+
+    IntRect absoluteImageRect() const { return m_absoluteImageRect; }
+
+    RenderingMode renderingMode() const { return m_renderingMode; }
+    const DestinationColorSpace& colorSpace() const { return m_colorSpace; }
+
+    ImageBuffer* imageBuffer();
+    PixelBuffer* pixelBuffer(AlphaPremultiplication);
+
+    std::optional<PixelBuffer> getPixelBuffer(AlphaPremultiplication, const IntRect& sourceRect, std::optional<DestinationColorSpace> = std::nullopt);
+    void copyPixelBuffer(PixelBuffer& destinationPixelBuffer, const IntRect& sourceRect);
+
+    void correctPremultipliedPixelBuffer();
+    void transformToColorSpace(const DestinationColorSpace&);
+
+private:
+    FilterImage(const IntRect& absoluteImageRect, RenderingMode, const DestinationColorSpace&);
+
+    std::optional<PixelBuffer>& pixelBufferSlot(AlphaPremultiplication);
+
+    bool requiresPixelBufferColorSpaceConversion(std::optional<DestinationColorSpace>) const;
+
+    IntRect m_absoluteImageRect;
+
+    RenderingMode m_renderingMode;
+    DestinationColorSpace m_colorSpace;
+
+    RefPtr<ImageBuffer> m_imageBuffer;
+    std::optional<PixelBuffer> m_unpremultipliedPixelBuffer;
+    std::optional<PixelBuffer> m_premultipliedPixelBuffer;
+};
+
+} // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -47,7 +47,7 @@
 
 bool SourceAlpha::platformApplySoftware(const Filter&)
 {
-    ImageBuffer* resultImage = createImageBufferResult();
+    ImageBuffer* resultImage = imageBufferResult();
     if (!resultImage)
         return false;
     

Modified: trunk/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp (286128 => 286129)


--- trunk/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -34,7 +34,6 @@
 SourceGraphic::SourceGraphic()
     : FilterEffect(FilterEffect::Type::SourceGraphic)
 {
-    setOperatingColorSpace(DestinationColorSpace::SRGB());
 }
 
 void SourceGraphic::determineAbsolutePaintRect(const Filter& filter)
@@ -45,7 +44,7 @@
 
 bool SourceGraphic::platformApplySoftware(const Filter& filter)
 {
-    ImageBuffer* resultImage = createImageBufferResult();
+    ImageBuffer* resultImage = imageBufferResult();
     ImageBuffer* sourceImage = filter.sourceImage();
     if (!resultImage || !sourceImage)
         return false;

Modified: trunk/Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp (286128 => 286129)


--- trunk/Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -241,7 +241,7 @@
         if (!lastEffect->hasResult()) {
             filterData.state = FilterData::Applying;
             filterData.filter->apply();
-            lastEffect->correctFilterResultIfNeeded();
+            lastEffect->correctPremultipliedResultIfNeeded();
             lastEffect->transformResultColorSpace(DestinationColorSpace::SRGB());
         }
         filterData.state = FilterData::Built;

Modified: trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.cpp (286128 => 286129)


--- trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.cpp	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.cpp	2021-11-23 09:22:16 UTC (rev 286129)
@@ -76,10 +76,7 @@
 
 bool FEImage::platformApplySoftware(const Filter& filter)
 {
-    // FEImage results are always in DestinationColorSpace::SRGB()
-    setResultColorSpace(DestinationColorSpace::SRGB());
-
-    ImageBuffer* resultImage = createImageBufferResult();
+    auto resultImage = imageBufferResult();
     if (!resultImage)
         return false;
 

Modified: trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.h (286128 => 286129)


--- trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.h	2021-11-23 08:57:03 UTC (rev 286128)
+++ trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.h	2021-11-23 09:22:16 UTC (rev 286129)
@@ -44,6 +44,9 @@
 private:
     FEImage(SourceImage&&, const FloatRect& sourceImageRect, const SVGPreserveAspectRatioValue&);
 
+    // FEImage results are always in DestinationColorSpace::SRGB()
+    void setOperatingColorSpace(const DestinationColorSpace&) override { }
+
     void determineAbsolutePaintRect(const Filter&) final;
 
     bool platformApplySoftware(const Filter&) final;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to