<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[286129] trunk/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/286129">286129</a></dd>
<dt>Author</dt> <dd>said@apple.com</dd>
<dt>Date</dt> <dd>2021-11-23 01:22:16 -0800 (Tue, 23 Nov 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>[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:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreSourcestxt">trunk/Source/WebCore/Sources.txt</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicscoreimageFilterEffectRendererCoreImagemm">trunk/Source/WebCore/platform/graphics/coreimage/FilterEffectRendererCoreImage.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicscpuarmfiltersFEBlendNEONh">trunk/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEBlendcpp">trunk/Source/WebCore/platform/graphics/filters/FEBlend.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEColorMatrixcpp">trunk/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEComponentTransfercpp">trunk/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFECompositecpp">trunk/Source/WebCore/platform/graphics/filters/FEComposite.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFECompositeh">trunk/Source/WebCore/platform/graphics/filters/FEComposite.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEConvolveMatrixcpp">trunk/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEDisplacementMapcpp">trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEDisplacementMaph">trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEDropShadowcpp">trunk/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEFloodcpp">trunk/Source/WebCore/platform/graphics/filters/FEFlood.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEFloodh">trunk/Source/WebCore/platform/graphics/filters/FEFlood.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEGaussianBlurcpp">trunk/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFELightingcpp">trunk/Source/WebCore/platform/graphics/filters/FELighting.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEMergecpp">trunk/Source/WebCore/platform/graphics/filters/FEMerge.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEMorphologycpp">trunk/Source/WebCore/platform/graphics/filters/FEMorphology.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEMorphologyh">trunk/Source/WebCore/platform/graphics/filters/FEMorphology.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFEOffsetcpp">trunk/Source/WebCore/platform/graphics/filters/FEOffset.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFETilecpp">trunk/Source/WebCore/platform/graphics/filters/FETile.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFETurbulencecpp">trunk/Source/WebCore/platform/graphics/filters/FETurbulence.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFilterEffectcpp">trunk/Source/WebCore/platform/graphics/filters/FilterEffect.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFilterEffecth">trunk/Source/WebCore/platform/graphics/filters/FilterEffect.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersSourceAlphacpp">trunk/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersSourceGraphiccpp">trunk/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp</a></li>
<li><a href="#trunkSourceWebCorerenderingsvgRenderSVGResourceFiltercpp">trunk/Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp</a></li>
<li><a href="#trunkSourceWebCoresvggraphicsfiltersSVGFEImagecpp">trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.cpp</a></li>
<li><a href="#trunkSourceWebCoresvggraphicsfiltersSVGFEImageh">trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFilterImagecpp">trunk/Source/WebCore/platform/graphics/filters/FilterImage.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfiltersFilterImageh">trunk/Source/WebCore/platform/graphics/filters/FilterImage.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1,3 +1,152 @@
</span><ins>+2021-11-23  Said Abou-Hallawa  <said@apple.com>
+
+        [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:
+
</ins><span class="cx"> 2021-11-22  Simon Fraser  <simon.fraser@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Have ScrollAnimator::scrollToPositionWithAnimation() take a clamping argument
</span></span></pre></div>
<a id="trunkSourceWebCoreSourcestxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Sources.txt (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -2103,6 +2103,7 @@
</span><span class="cx"> platform/graphics/filters/FilterEffect.cpp
</span><span class="cx"> platform/graphics/filters/FilterEffectRenderer.cpp
</span><span class="cx"> platform/graphics/filters/FilterFunction.cpp
</span><ins>+platform/graphics/filters/FilterImage.cpp
</ins><span class="cx"> platform/graphics/filters/FilterOperation.cpp
</span><span class="cx"> platform/graphics/filters/FilterOperations.cpp
</span><span class="cx"> platform/graphics/filters/PointLightSource.cpp
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -10867,6 +10867,8 @@
</span><span class="cx">          721B496F2512AC0400FE9D3B /* ImageBitmapBacking.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ImageBitmapBacking.cpp; sourceTree = "<group>"; };
</span><span class="cx">          721B49702512AC0400FE9D3B /* ImageBitmapBacking.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImageBitmapBacking.h; sourceTree = "<group>"; };
</span><span class="cx">          722A815C238FD50500C00583 /* AnimationFrameRate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AnimationFrameRate.h; sourceTree = "<group>"; };
</span><ins>+               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>"; };
</ins><span class="cx">           724ED3291A3A7E5400F5F13C /* EXTBlendMinMax.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EXTBlendMinMax.cpp; sourceTree = "<group>"; };
</span><span class="cx">          724ED32A1A3A7E5400F5F13C /* EXTBlendMinMax.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXTBlendMinMax.h; sourceTree = "<group>"; };
</span><span class="cx">          724ED32B1A3A7E5400F5F13C /* EXTBlendMinMax.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EXTBlendMinMax.idl; sourceTree = "<group>"; };
</span><span class="lines">@@ -25945,6 +25947,8 @@
</span><span class="cx">                          7214B9B7274458FA003BE6DF /* FilterEffectVector.h */,
</span><span class="cx">                          7262D757272A174100C56A09 /* FilterFunction.cpp */,
</span><span class="cx">                          7262D756272A174100C56A09 /* FilterFunction.h */,
</span><ins>+                               72435EF5273D07670005E7EE /* FilterImage.cpp */,
+                               72435EF4273D07670005E7EE /* FilterImage.h */,
</ins><span class="cx">                           49ECEB631499790D00CDD3A4 /* FilterOperation.cpp */,
</span><span class="cx">                          49ECEB641499790D00CDD3A4 /* FilterOperation.h */,
</span><span class="cx">                          49ECEB651499790D00CDD3A4 /* FilterOperations.cpp */,
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicscoreimageFilterEffectRendererCoreImagemm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/coreimage/FilterEffectRendererCoreImage.mm (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -116,7 +116,6 @@
</span><span class="cx">         inputImages.append(inputImage);
</span><span class="cx">     }
</span><span class="cx">     effect.determineAbsolutePaintRect(filter);
</span><del>-    effect.setResultColorSpace(effect.operatingColorSpace());
</del><span class="cx">     
</span><span class="cx">     if (effect.absolutePaintRect().isEmpty() || ImageBuffer::sizeNeedsClamping(effect.absolutePaintRect().size()))
</span><span class="cx">         return nullptr;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicscpuarmfiltersFEBlendNEONh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/cpu/arm/filters/FEBlendNEON.h (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -111,7 +111,7 @@
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx">     FilterEffect* in2 = inputEffect(1);
</span><span class="cx"> 
</span><del>-    auto& destinationPixelBuffer = createPremultipliedImageResult();
</del><ins>+    auto& destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
</ins><span class="cx">     if (!destinationPixelBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="lines">@@ -118,10 +118,10 @@
</span><span class="cx">     auto& destinationPixelArray = destinationPixelBuffer->data();
</span><span class="cx"> 
</span><span class="cx">     IntRect effectADrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
</span><del>-    auto sourcePixelArrayA = in->premultipliedResult(effectADrawingRect);
</del><ins>+    auto sourcePixelArrayA = in->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectADrawingRect);
</ins><span class="cx"> 
</span><span class="cx">     IntRect effectBDrawingRect = requestedRegionOfInputPixelBuffer(in2->absolutePaintRect());
</span><del>-    auto sourcePixelArrayB = in2->premultipliedResult(effectBDrawingRect);
</del><ins>+    auto sourcePixelArrayB = in2->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectBDrawingRect);
</ins><span class="cx"> 
</span><span class="cx">     unsigned sourcePixelArrayLength = sourcePixelArrayA->length();
</span><span class="cx">     ASSERT(pixelArrayLength == sourcePixelArrayB->length());
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEBlendcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEBlend.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -59,16 +59,16 @@
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx">     FilterEffect* in2 = inputEffect(1);
</span><span class="cx"> 
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
</del><ins>+    auto resultImage = imageBufferResult();
</ins><span class="cx">     if (!resultImage)
</span><span class="cx">         return false;
</span><del>-    GraphicsContext& filterContext = resultImage->context();
</del><span class="cx"> 
</span><del>-    ImageBuffer* imageBuffer = in->imageBufferResult();
-    ImageBuffer* imageBuffer2 = in2->imageBufferResult();
</del><ins>+    auto imageBuffer = in->imageBufferResult();
+    auto imageBuffer2 = in2->imageBufferResult();
</ins><span class="cx">     if (!imageBuffer || !imageBuffer2)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><ins>+    GraphicsContext& filterContext = resultImage->context();
</ins><span class="cx">     filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
</span><span class="cx">     filterContext.drawImageBuffer(*imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), { { }, imageBuffer->logicalSize() }, { CompositeOperator::SourceOver, m_mode });
</span><span class="cx">     return true;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEColorMatrixcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEColorMatrix.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -277,11 +277,11 @@
</span><span class="cx"> {
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx"> 
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
</del><ins>+    auto resultImage = imageBufferResult();
</ins><span class="cx">     if (!resultImage)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    ImageBuffer* inBuffer = in->imageBufferResult();
</del><ins>+    auto inBuffer = in->imageBufferResult();
</ins><span class="cx">     if (inBuffer)
</span><span class="cx">         resultImage->context().drawImageBuffer(*inBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEComponentTransfercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEComponentTransfer.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -105,12 +105,10 @@
</span><span class="cx"> {
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx"> 
</span><del>-    auto& destinationPixelBuffer = createUnmultipliedImageResult();
</del><ins>+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Unpremultiplied);
</ins><span class="cx">     if (!destinationPixelBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
</del><span class="cx">     LookupTable redTable;
</span><span class="cx">     LookupTable greenTable;
</span><span class="cx">     LookupTable blueTable;
</span><span class="lines">@@ -118,9 +116,12 @@
</span><span class="cx">     computeLookupTables(redTable, greenTable, blueTable, alphaTable);
</span><span class="cx"> 
</span><span class="cx">     IntRect drawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
</span><del>-    in->copyUnmultipliedResult(destinationPixelArray, drawingRect, operatingColorSpace());
</del><ins>+    in->copyPixelBufferResult(*destinationPixelBuffer, drawingRect);
+    
+    auto& destinationPixelArray = destinationPixelBuffer->data();
+    uint8_t* data = destinationPixelArray.data();
</ins><span class="cx">     unsigned destinationPixelArrayLength = destinationPixelArray.length();
</span><del>-    uint8_t* data = destinationPixelArray.data();
</del><ins>+
</ins><span class="cx">     for (unsigned pixelOffset = 0; pixelOffset < destinationPixelArrayLength; pixelOffset += 4) {
</span><span class="cx">         data[pixelOffset] = redTable[data[pixelOffset]];
</span><span class="cx">         data[pixelOffset + 1] = greenTable[data[pixelOffset + 1]];
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFECompositecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEComposite.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -87,14 +87,6 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void FEComposite::correctFilterResultIfNeeded()
-{
-    if (m_type != FECOMPOSITE_OPERATOR_ARITHMETIC)
-        return;
-
-    forceValidPreMultipliedPixels();
-}
-    
</del><span class="cx"> static unsigned char clampByte(int c)
</span><span class="cx"> {
</span><span class="cx">     unsigned char buff[] = { static_cast<unsigned char>(c), 255, 0 };
</span><span class="lines">@@ -229,34 +221,35 @@
</span><span class="cx">     FilterEffect* in2 = inputEffect(1);
</span><span class="cx"> 
</span><span class="cx">     if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) {
</span><del>-        auto& destinationPixelBuffer = createPremultipliedImageResult();
</del><ins>+        auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
</ins><span class="cx">         if (!destinationPixelBuffer)
</span><span class="cx">             return false;
</span><span class="cx">         
</span><del>-        auto& destinationPixelArray = destinationPixelBuffer->data();
-
</del><span class="cx">         IntRect effectADrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
</span><del>-        auto sourcePixelArray = in->premultipliedResult(effectADrawingRect, operatingColorSpace());
-        if (!sourcePixelArray)
</del><ins>+        auto sourcePixelBuffer = in->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectADrawingRect, operatingColorSpace());
+        if (!sourcePixelBuffer)
</ins><span class="cx">             return false;
</span><span class="cx"> 
</span><span class="cx">         IntRect effectBDrawingRect = requestedRegionOfInputPixelBuffer(in2->absolutePaintRect());
</span><del>-        in2->copyPremultipliedResult(destinationPixelArray, effectBDrawingRect, operatingColorSpace());
</del><ins>+        in2->copyPixelBufferResult(*destinationPixelBuffer, effectBDrawingRect);
</ins><span class="cx"> 
</span><del>-        platformArithmeticSoftware(*sourcePixelArray, destinationPixelArray, m_k1, m_k2, m_k3, m_k4);
</del><ins>+        auto& sourcePixelArray = sourcePixelBuffer->data();
+        auto& destinationPixelArray = destinationPixelBuffer->data();
+        platformArithmeticSoftware(sourcePixelArray, destinationPixelArray, m_k1, m_k2, m_k3, m_k4);
</ins><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
</del><ins>+    auto resultImage = imageBufferResult();
</ins><span class="cx">     if (!resultImage)
</span><span class="cx">         return false;
</span><del>-    GraphicsContext& filterContext = resultImage->context();
</del><span class="cx"> 
</span><del>-    ImageBuffer* imageBuffer = in->imageBufferResult();
-    ImageBuffer* imageBuffer2 = in2->imageBufferResult();
</del><ins>+    auto imageBuffer = in->imageBufferResult();
+    auto imageBuffer2 = in2->imageBufferResult();
</ins><span class="cx">     if (!imageBuffer || !imageBuffer2)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><ins>+    GraphicsContext& filterContext = resultImage->context();
+
</ins><span class="cx">     switch (m_type) {
</span><span class="cx">     case FECOMPOSITE_OPERATOR_OVER:
</span><span class="cx">         filterContext.drawImageBuffer(*imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()));
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFECompositeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEComposite.h (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -59,12 +59,10 @@
</span><span class="cx"> private:
</span><span class="cx">     FEComposite(const CompositeOperationType&, float k1, float k2, float k3, float k4);
</span><span class="cx"> 
</span><del>-    void correctFilterResultIfNeeded() override;
</del><ins>+    void determineAbsolutePaintRect(const Filter&) override;
</ins><span class="cx"> 
</span><del>-    bool requiresValidPreMultipliedPixels() override { return m_type != FECOMPOSITE_OPERATOR_ARITHMETIC; }
</del><ins>+    bool mayProduceInvalidPremultipliedPixels() const override { return m_type == FECOMPOSITE_OPERATOR_ARITHMETIC; }
</ins><span class="cx"> 
</span><del>-    void determineAbsolutePaintRect(const Filter&) override;
-
</del><span class="cx">     bool platformApplySoftware(const Filter&) override;
</span><span class="cx"> 
</span><span class="cx">     WTF::TextStream& externalRepresentation(WTF::TextStream&, RepresentationType) const override;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEConvolveMatrixcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -365,26 +365,23 @@
</span><span class="cx"> {
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx"> 
</span><del>-    auto& destinationPixelBuffer = m_preserveAlpha ? createUnmultipliedImageResult() : createPremultipliedImageResult();
</del><ins>+    auto alphaFormat = m_preserveAlpha ? AlphaPremultiplication::Unpremultiplied : AlphaPremultiplication::Premultiplied;
+    auto destinationPixelBuffer = pixelBufferResult(alphaFormat);
</ins><span class="cx">     if (!destinationPixelBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
</del><span class="cx">     IntRect effectDrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
</span><del>-
-    RefPtr<Uint8ClampedArray> sourcePixelArray;
-    if (m_preserveAlpha)
-        sourcePixelArray = in->unmultipliedResult(effectDrawingRect, operatingColorSpace());
-    else
-        sourcePixelArray = in->premultipliedResult(effectDrawingRect, operatingColorSpace());
-    if (!sourcePixelArray)
</del><ins>+    auto sourcePixelBuffer = in->getPixelBufferResult(alphaFormat, effectDrawingRect, operatingColorSpace());
+    if (!sourcePixelBuffer)
</ins><span class="cx">         return false;
</span><span class="cx"> 
</span><ins>+    auto& sourcePixelArray = sourcePixelBuffer->data();
+    auto& destinationPixelArray = destinationPixelBuffer->data();
+
</ins><span class="cx">     IntSize paintSize = absolutePaintRect().size();
</span><span class="cx">     
</span><span class="cx">     PaintingData paintingData = {
</span><del>-        *sourcePixelArray,
</del><ins>+        sourcePixelArray,
</ins><span class="cx">         destinationPixelArray,
</span><span class="cx">         paintSize.width(),
</span><span class="cx">         paintSize.height(),
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEDisplacementMapcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -68,13 +68,13 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void FEDisplacementMap::setResultColorSpace(const DestinationColorSpace&)
</del><ins>+const DestinationColorSpace& FEDisplacementMap::resultColorSpace() const
</ins><span class="cx"> {
</span><span class="cx">     // Spec: The 'color-interpolation-filters' property only applies to the 'in2' source image
</span><span class="cx">     // and does not apply to the 'in' source image. The 'in' source image must remain in its
</span><span class="cx">     // current color space.
</span><del>-    // The result is in that smae color space because it is a displacement of the 'in' image.
-    FilterEffect::setResultColorSpace(inputEffect(0)->resultColorSpace());
</del><ins>+    // The result is in that same color space because it is a displacement of the 'in' image.
+    return inputEffect(0)->resultColorSpace();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void FEDisplacementMap::transformResultColorSpace(FilterEffect* in, const int index)
</span><span class="lines">@@ -98,7 +98,7 @@
</span><span class="cx">     ASSERT(m_xChannelSelector != CHANNEL_UNKNOWN);
</span><span class="cx">     ASSERT(m_yChannelSelector != CHANNEL_UNKNOWN);
</span><span class="cx"> 
</span><del>-    auto& destinationPixelBuffer = createPremultipliedImageResult();
</del><ins>+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
</ins><span class="cx">     if (!destinationPixelBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="lines">@@ -105,16 +105,18 @@
</span><span class="cx">     auto& destinationPixelArray = destinationPixelBuffer->data();
</span><span class="cx"> 
</span><span class="cx">     IntRect effectADrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
</span><del>-    auto inputImage = in->premultipliedResult(effectADrawingRect);
</del><ins>+    auto inputPixelBuffer = in->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectADrawingRect);
</ins><span class="cx"> 
</span><span class="cx">     IntRect effectBDrawingRect = requestedRegionOfInputPixelBuffer(in2->absolutePaintRect());
</span><span class="cx">     // The calculations using the pixel values from ‘in2’ are performed using non-premultiplied color values.
</span><del>-    auto displacementImage = in2->unmultipliedResult(effectBDrawingRect);
</del><ins>+    auto displacementPixelBuffer = in2->getPixelBufferResult(AlphaPremultiplication::Unpremultiplied, effectBDrawingRect);
</ins><span class="cx">     
</span><del>-    if (!inputImage || !displacementImage)
</del><ins>+    if (!inputPixelBuffer || !displacementPixelBuffer)
</ins><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    ASSERT(inputImage->length() == displacementImage->length());
</del><ins>+    auto& inputImage = inputPixelBuffer->data();
+    auto& displacementImage = displacementPixelBuffer->data();
+    ASSERT(inputImage.length() == displacementImage.length());
</ins><span class="cx"> 
</span><span class="cx">     IntSize paintSize = absolutePaintRect().size();
</span><span class="cx"> 
</span><span class="lines">@@ -135,8 +137,8 @@
</span><span class="cx">         for (int x = 0; x < paintSize.width(); ++x) {
</span><span class="cx">             int destinationIndex = lineStartOffset + x * 4;
</span><span class="cx">             
</span><del>-            int srcX = x + static_cast<int>(scaleForColorX * displacementImage->item(destinationIndex + displacementChannelX) + scaledOffsetX);
-            int srcY = y + static_cast<int>(scaleForColorY * displacementImage->item(destinationIndex + displacementChannelY) + scaledOffsetY);
</del><ins>+            int srcX = x + static_cast<int>(scaleForColorX * displacementImage.item(destinationIndex + displacementChannelX) + scaledOffsetX);
+            int srcY = y + static_cast<int>(scaleForColorY * displacementImage.item(destinationIndex + displacementChannelY) + scaledOffsetY);
</ins><span class="cx"> 
</span><span class="cx">             unsigned* destinationPixelPtr = reinterpret_cast<unsigned*>(destinationPixelArray.data() + destinationIndex);
</span><span class="cx">             if (srcX < 0 || srcX >= paintSize.width() || srcY < 0 || srcY >= paintSize.height()) {
</span><span class="lines">@@ -144,7 +146,7 @@
</span><span class="cx">                 continue;
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            *destinationPixelPtr = *reinterpret_cast<unsigned*>(inputImage->data() + byteOffsetOfPixel(srcX, srcY, rowBytes));
</del><ins>+            *destinationPixelPtr = *reinterpret_cast<unsigned*>(inputImage.data() + byteOffsetOfPixel(srcX, srcY, rowBytes));
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEDisplacementMaph"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEDisplacementMap.h (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -47,19 +47,19 @@
</span><span class="cx">     float scale() const { return m_scale; }
</span><span class="cx">     bool setScale(float);
</span><span class="cx"> 
</span><del>-    void setResultColorSpace(const DestinationColorSpace&) override;
-    void transformResultColorSpace(FilterEffect*, const int) override;
-
</del><span class="cx"> private:
</span><span class="cx">     FEDisplacementMap(ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, float);
</span><span class="cx"> 
</span><del>-    bool platformApplySoftware(const Filter&) override;
</del><ins>+    int xChannelIndex() const { return m_xChannelSelector - 1; }
+    int yChannelIndex() const { return m_yChannelSelector - 1; }
</ins><span class="cx"> 
</span><span class="cx">     void determineAbsolutePaintRect(const Filter&) override { setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); }
</span><span class="cx"> 
</span><del>-    int xChannelIndex() const { return m_xChannelSelector - 1; }
-    int yChannelIndex() const { return m_yChannelSelector - 1; }
</del><ins>+    const DestinationColorSpace& resultColorSpace() const override;
+    void transformResultColorSpace(FilterEffect*, const int) override;
</ins><span class="cx"> 
</span><ins>+    bool platformApplySoftware(const Filter&) override;
+
</ins><span class="cx">     WTF::TextStream& externalRepresentation(WTF::TextStream&, RepresentationType) const override;
</span><span class="cx"> 
</span><span class="cx">     ChannelSelectorType m_xChannelSelector;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEDropShadowcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEDropShadow.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -73,7 +73,7 @@
</span><span class="cx"> {
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx"> 
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
</del><ins>+    auto resultImage = imageBufferResult();
</ins><span class="cx">     if (!resultImage)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="lines">@@ -84,7 +84,7 @@
</span><span class="cx">     FloatRect drawingRegionWithOffset(drawingRegion);
</span><span class="cx">     drawingRegionWithOffset.move(offset);
</span><span class="cx"> 
</span><del>-    ImageBuffer* sourceImage = in->imageBufferResult();
</del><ins>+    auto sourceImage = in->imageBufferResult();
</ins><span class="cx">     if (!sourceImage)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEFloodcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEFlood.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -60,7 +60,7 @@
</span><span class="cx"> 
</span><span class="cx"> bool FEFlood::platformApplySoftware(const Filter&)
</span><span class="cx"> {
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
</del><ins>+    auto resultImage = imageBufferResult();
</ins><span class="cx">     if (!resultImage)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEFloodh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEFlood.h (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -39,8 +39,7 @@
</span><span class="cx"> #if !USE(CG)
</span><span class="cx">     // feFlood does not perform color interpolation of any kind, so the result is always in the current
</span><span class="cx">     // color space regardless of the value of color-interpolation-filters.
</span><del>-    void setOperatingColorSpace(const DestinationColorSpace&) override { FilterEffect::setResultColorSpace(DestinationColorSpace::SRGB()); }
-    void setResultColorSpace(const DestinationColorSpace&) override { FilterEffect::setResultColorSpace(DestinationColorSpace::SRGB()); }
</del><ins>+    void setOperatingColorSpace(const DestinationColorSpace&) override { }
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> private:
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEGaussianBlurcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -523,16 +523,14 @@
</span><span class="cx"> {
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx"> 
</span><del>-    auto& destinationPixelBuffer = createPremultipliedImageResult();
</del><ins>+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
</ins><span class="cx">     if (!destinationPixelBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
</del><span class="cx">     setIsAlphaImage(in->isAlphaImage());
</span><span class="cx"> 
</span><span class="cx">     IntRect effectDrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
</span><del>-    in->copyPremultipliedResult(destinationPixelArray, effectDrawingRect, operatingColorSpace());
</del><ins>+    in->copyPixelBufferResult(*destinationPixelBuffer, effectDrawingRect);
</ins><span class="cx">     if (!m_stdX && !m_stdY)
</span><span class="cx">         return true;
</span><span class="cx"> 
</span><span class="lines">@@ -543,6 +541,7 @@
</span><span class="cx">     if (!tmpImageData)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><ins>+    auto& destinationPixelArray = destinationPixelBuffer->data();
</ins><span class="cx">     platformApply(destinationPixelArray, *tmpImageData, kernelSize.width(), kernelSize.height(), paintSize);
</span><span class="cx">     return true;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFELightingcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FELighting.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -475,16 +475,15 @@
</span><span class="cx"> {
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx"> 
</span><del>-    auto& destinationPixelBuffer = createPremultipliedImageResult();
</del><ins>+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
</ins><span class="cx">     if (!destinationPixelBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
</del><span class="cx">     setIsAlphaImage(false);
</span><span class="cx"> 
</span><span class="cx">     IntRect effectDrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
</span><del>-    in->copyPremultipliedResult(destinationPixelArray, effectDrawingRect, operatingColorSpace());
</del><ins>+    in->copyPixelBufferResult(*destinationPixelBuffer, effectDrawingRect);
+
</ins><span class="cx">     // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3
</span><span class="cx">     // standard has no test case for them, and other browsers (like Firefox) has strange
</span><span class="cx">     // output for various kernelUnitLengths, and I am not sure they are reliable.
</span><span class="lines">@@ -491,6 +490,8 @@
</span><span class="cx">     // Anyway, feConvolveMatrix should also use the implementation
</span><span class="cx"> 
</span><span class="cx">     IntSize absolutePaintSize = absolutePaintRect().size();
</span><ins>+    auto& destinationPixelArray = destinationPixelBuffer->data();
+
</ins><span class="cx">     drawLighting(destinationPixelArray, absolutePaintSize.width(), absolutePaintSize.height());
</span><span class="cx">     return true;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEMergecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEMerge.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -43,7 +43,7 @@
</span><span class="cx">     unsigned size = numberOfEffectInputs();
</span><span class="cx">     ASSERT(size > 0);
</span><span class="cx"> 
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
</del><ins>+    auto resultImage = imageBufferResult();
</ins><span class="cx">     if (!resultImage)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="lines">@@ -50,7 +50,7 @@
</span><span class="cx">     GraphicsContext& filterContext = resultImage->context();
</span><span class="cx">     for (unsigned i = 0; i < size; ++i) {
</span><span class="cx">         FilterEffect* in = inputEffect(i);
</span><del>-        if (ImageBuffer* inBuffer = in->imageBufferResult())
</del><ins>+        if (auto inBuffer = in->imageBufferResult())
</ins><span class="cx">             filterContext.drawImageBuffer(*inBuffer, drawingRegionOfInputImage(in->absolutePaintRect()));
</span><span class="cx">     }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEMorphologycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEMorphology.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -229,14 +229,9 @@
</span><span class="cx">     platformApplyGeneric(paintingData, 0, paintingData.height);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool FEMorphology::platformApplyDegenerate(Uint8ClampedArray& dstPixelArray, const IntRect& imageRect, int radiusX, int radiusY)
</del><ins>+bool FEMorphology::isDegenerate(int radiusX, int radiusY) const
</ins><span class="cx"> {
</span><del>-    if (radiusX < 0 || radiusY < 0 || (!radiusX && !radiusY)) {
-        FilterEffect* in = inputEffect(0);
-        in->copyPremultipliedResult(dstPixelArray, imageRect, operatingColorSpace());
-        return true;
-    }
-    return false;
</del><ins>+    return radiusX < 0 || radiusY < 0 || (!radiusX && !radiusY);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool FEMorphology::platformApplySoftware(const Filter& filter)
</span><span class="lines">@@ -243,33 +238,38 @@
</span><span class="cx"> {
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx"> 
</span><del>-    auto& destinationPixelBuffer = createPremultipliedImageResult();
</del><ins>+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Premultiplied);
</ins><span class="cx">     if (!destinationPixelBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    auto& destinationPixelArray = destinationPixelBuffer->data();
-
</del><span class="cx">     setIsAlphaImage(in->isAlphaImage());
</span><span class="cx"> 
</span><span class="cx">     IntRect effectDrawingRect = requestedRegionOfInputPixelBuffer(in->absolutePaintRect());
</span><span class="cx"> 
</span><span class="cx">     IntSize radius = flooredIntSize(FloatSize(m_radiusX, m_radiusY));
</span><del>-    if (platformApplyDegenerate(destinationPixelArray, effectDrawingRect, radius.width(), radius.height()))
</del><ins>+    if (isDegenerate(radius.width(), radius.height())) {
+        in->copyPixelBufferResult(*destinationPixelBuffer, effectDrawingRect);
</ins><span class="cx">         return true;
</span><ins>+    }
</ins><span class="cx"> 
</span><del>-    auto sourcePixelArray = in->premultipliedResult(effectDrawingRect, operatingColorSpace());
-    if (!sourcePixelArray)
-        return false;
-
</del><span class="cx">     radius = flooredIntSize(filter.scaledByFilterScale({ m_radiusX, m_radiusY }));
</span><span class="cx">     int radiusX = std::min(effectDrawingRect.width() - 1, radius.width());
</span><span class="cx">     int radiusY = std::min(effectDrawingRect.height() - 1, radius.height());
</span><span class="cx"> 
</span><del>-    if (platformApplyDegenerate(destinationPixelArray, effectDrawingRect, radiusX, radiusY))
</del><ins>+    if (isDegenerate(radiusX, radiusY)) {
+        in->copyPixelBufferResult(*destinationPixelBuffer, effectDrawingRect);
</ins><span class="cx">         return true;
</span><del>-    
</del><ins>+    }
+
+    auto sourcePixelBuffer = in->getPixelBufferResult(AlphaPremultiplication::Premultiplied, effectDrawingRect, operatingColorSpace());
+    if (!sourcePixelBuffer)
+        return false;
+
+    auto& sourcePixelArray = sourcePixelBuffer->data();
+    auto& destinationPixelArray = destinationPixelBuffer->data();
+
</ins><span class="cx">     PaintingData paintingData;
</span><del>-    paintingData.srcPixelArray = sourcePixelArray.get();
</del><ins>+    paintingData.srcPixelArray = &sourcePixelArray;
</ins><span class="cx">     paintingData.dstPixelArray = &destinationPixelArray;
</span><span class="cx">     paintingData.width = ceilf(effectDrawingRect.width());
</span><span class="cx">     paintingData.height = ceilf(effectDrawingRect.height());
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEMorphologyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEMorphology.h (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -53,7 +53,7 @@
</span><span class="cx"> 
</span><span class="cx">     WTF::TextStream& externalRepresentation(WTF::TextStream&, RepresentationType) const override;
</span><span class="cx"> 
</span><del>-    bool platformApplyDegenerate(Uint8ClampedArray& dstPixelArray, const IntRect& imageRect, int radiusX, int radiusY);
</del><ins>+    bool isDegenerate(int radiusX, int radiusY) const;
</ins><span class="cx"> 
</span><span class="cx">     struct PaintingData {
</span><span class="cx">         const Uint8ClampedArray* srcPixelArray;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFEOffsetcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FEOffset.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -68,8 +68,8 @@
</span><span class="cx"> {
</span><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx"> 
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
-    ImageBuffer* inBuffer = in->imageBufferResult();
</del><ins>+    auto resultImage = imageBufferResult();
+    auto inBuffer = in->imageBufferResult();
</ins><span class="cx">     if (!resultImage || !inBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFETilecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FETile.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -44,11 +44,10 @@
</span><span class="cx"> 
</span><span class="cx"> bool FETile::platformApplySoftware(const Filter& filter)
</span><span class="cx"> {
</span><del>-// FIXME: See bug 47315. This is a hack to work around a compile failure, but is incorrect behavior otherwise.
</del><span class="cx">     FilterEffect* in = inputEffect(0);
</span><span class="cx"> 
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
-    ImageBuffer* inBuffer = in->imageBufferResult();
</del><ins>+    auto resultImage = imageBufferResult();
+    auto inBuffer = in->imageBufferResult();
</ins><span class="cx">     if (!resultImage || !inBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFETurbulencecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FETurbulence.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -395,7 +395,7 @@
</span><span class="cx"> 
</span><span class="cx"> bool FETurbulence::platformApplySoftware(const Filter& filter)
</span><span class="cx"> {
</span><del>-    auto& destinationPixelBuffer = createUnmultipliedImageResult();
</del><ins>+    auto destinationPixelBuffer = pixelBufferResult(AlphaPremultiplication::Unpremultiplied);
</ins><span class="cx">     if (!destinationPixelBuffer)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFilterEffectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FilterEffect.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -27,29 +27,13 @@
</span><span class="cx"> #include "Color.h"
</span><span class="cx"> #include "Filter.h"
</span><span class="cx"> #include "GeometryUtilities.h"
</span><del>-#include "GraphicsContext.h"
</del><span class="cx"> #include "ImageBuffer.h"
</span><span class="cx"> #include "Logging.h"
</span><span class="cx"> #include "PixelBuffer.h"
</span><del>-#include <JavaScriptCore/JSCInlines.h>
-#include <JavaScriptCore/TypedArrayInlines.h>
</del><span class="cx"> #include <wtf/text/TextStream.h>
</span><span class="cx"> 
</span><del>-#if HAVE(ARM_NEON_INTRINSICS)
-#include <arm_neon.h>
-#endif
-
-#if USE(ACCELERATE)
-#include <Accelerate/Accelerate.h>
-#endif
-
</del><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-FilterEffect::FilterEffect(FilterEffect::Type type)
-    : FilterFunction(type)
-{
-}
-
</del><span class="cx"> void FilterEffect::determineAbsolutePaintRect(const Filter&)
</span><span class="cx"> {
</span><span class="cx">     m_absolutePaintRect = IntRect();
</span><span class="lines">@@ -157,7 +141,6 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     determineAbsolutePaintRect(filter);
</span><del>-    setResultColorSpace(m_operatingColorSpace);
</del><span class="cx"> 
</span><span class="cx">     LOG_WITH_STREAM(Filters, stream
</span><span class="cx">         << "FilterEffect " << filterName() << " " << this << " apply():"
</span><span class="lines">@@ -171,69 +154,27 @@
</span><span class="cx">     if (m_absolutePaintRect.isEmpty() || ImageBuffer::sizeNeedsClamping(m_absolutePaintRect.size()))
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    if (requiresValidPreMultipliedPixels()) {
-        for (unsigned i = 0; i < size; ++i)
-            inputEffect(i)->correctFilterResultIfNeeded();
</del><ins>+    if (!mayProduceInvalidPremultipliedPixels()) {
+        for (auto& in : m_inputEffects)
+            in->correctPremultipliedResultIfNeeded();
</ins><span class="cx">     }
</span><ins>+    
+    if (!createResult())
+        return false;
</ins><span class="cx"> 
</span><span class="cx">     // Add platform specific apply functions here and return earlier.
</span><span class="cx">     return platformApplySoftware(filter);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void FilterEffect::forceValidPreMultipliedPixels()
</del><ins>+bool FilterEffect::createResult()
</ins><span class="cx"> {
</span><del>-    // 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;
-    }
</del><ins>+    m_filterImage = FilterImage::create(m_absolutePaintRect, RenderingMode::Unaccelerated, resultColorSpace());
+    return m_filterImage;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void FilterEffect::clearResult()
</span><span class="cx"> {
</span><del>-    m_imageBufferResult = nullptr;
-    m_unmultipliedImageResult = std::nullopt;
-    m_premultipliedImageResult = std::nullopt;
</del><ins>+    m_filterImage = nullptr;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void FilterEffect::clearResultsRecursive()
</span><span class="lines">@@ -240,399 +181,49 @@
</span><span class="cx"> {
</span><span class="cx">     // Clear all results, regardless that the current effect has
</span><span class="cx">     // a result. Can be used if an effect is in an erroneous state.
</span><del>-    if (hasResult())
-        clearResult();
-
-    unsigned size = m_inputEffects.size();
-    for (unsigned i = 0; i < size; ++i)
-        m_inputEffects.at(i).get()->clearResultsRecursive();
</del><ins>+    clearResult();
+    for (auto& effect : m_inputEffects)
+        effect->clearResultsRecursive();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> ImageBuffer* FilterEffect::imageBufferResult()
</span><span class="cx"> {
</span><del>-    LOG_WITH_STREAM(Filters, stream << "FilterEffect " << filterName() << " " << this << " imageBufferResult(). Existing image buffer " << m_imageBufferResult.get() <<  " m_premultipliedImageResult " << m_premultipliedImageResult << " m_unmultipliedImageResult " << m_unmultipliedImageResult);
-
</del><span class="cx">     if (!hasResult())
</span><span class="cx">         return nullptr;
</span><del>-
-    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();
</del><ins>+    return m_filterImage->imageBuffer();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-RefPtr<Uint8ClampedArray> FilterEffect::unmultipliedResult(const IntRect& rect, std::optional<DestinationColorSpace> colorSpace)
</del><ins>+PixelBuffer* FilterEffect::pixelBufferResult(AlphaPremultiplication alphaFormat)
</ins><span class="cx"> {
</span><del>-    IntSize scaledSize(rect.size());
-    ASSERT(!ImageBuffer::sizeNeedsClamping(scaledSize));
-    auto checkedArea = scaledSize.area<RecordOverflow>() * 4;
-    if (checkedArea.hasOverflowed())
</del><ins>+    if (!hasResult())
</ins><span class="cx">         return nullptr;
</span><del>-    auto pixelArray = Uint8ClampedArray::tryCreateUninitialized(checkedArea);
-    if (!pixelArray)
-        return nullptr;
-    copyUnmultipliedResult(*pixelArray, rect, colorSpace);
-    return pixelArray;
</del><ins>+    return m_filterImage->pixelBuffer(alphaFormat);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-RefPtr<Uint8ClampedArray> FilterEffect::premultipliedResult(const IntRect& rect, std::optional<DestinationColorSpace> colorSpace)
</del><ins>+std::optional<PixelBuffer> FilterEffect::getPixelBufferResult(AlphaPremultiplication alphaFormat, const IntRect& sourceRect, std::optional<DestinationColorSpace> colorSpace)
</ins><span class="cx"> {
</span><del>-    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;
</del><ins>+    ASSERT(hasResult());
+    return m_filterImage->getPixelBuffer(alphaFormat, sourceRect, colorSpace);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void FilterEffect::copyImageBytes(const Uint8ClampedArray& source, Uint8ClampedArray& destination, const IntRect& rect) const
</del><ins>+void FilterEffect::copyPixelBufferResult(PixelBuffer& destinationPixelBuffer, const IntRect& sourceRect) const
</ins><span class="cx"> {
</span><del>-    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 = reinterpret_cast<void*>(source.data());
-
-    vImage_Buffer dest;
-    dest.width = inputSize.width();
-    dest.height = inputSize.height();
-    dest.rowBytes = rowBytes;
-    dest.data = reinterpret_cast<void*>(destination.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 = reinterpret_cast<void*>(source.data());
-
-    vImage_Buffer dest;
-    dest.width = inputSize.width();
-    dest.height = inputSize.height();
-    dest.rowBytes = rowBytes;
-    dest.data = reinterpret_cast<void*>(destination.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)
-{
</del><span class="cx">     ASSERT(hasResult());
</span><del>-    
-    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);
</del><ins>+    m_filterImage->copyPixelBuffer(destinationPixelBuffer, sourceRect);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void FilterEffect::copyPremultipliedResult(Uint8ClampedArray& destination, const IntRect& rect, std::optional<DestinationColorSpace> colorSpace)
</del><ins>+void FilterEffect::correctPremultipliedResultIfNeeded()
</ins><span class="cx"> {
</span><del>-    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);
</del><ins>+    if (!hasResult() || !mayProduceInvalidPremultipliedPixels())
</ins><span class="cx">         return;
</span><del>-    }
-    copyImageBytes(m_premultipliedImageResult->data(), destination, rect);
</del><ins>+    m_filterImage->correctPremultipliedPixelBuffer();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-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
-}
-
</del><span class="cx"> void FilterEffect::transformResultColorSpace(const DestinationColorSpace& destinationColorSpace)
</span><span class="cx"> {
</span><del>-#if USE(CG)
-    // CG handles color space adjustments internally.
-    UNUSED_PARAM(destinationColorSpace);
-#else
-    if (!hasResult() || destinationColorSpace == m_resultColorSpace)
</del><ins>+    if (!hasResult())
</ins><span class="cx">         return;
</span><del>-
-    // 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
</del><ins>+    m_filterImage->transformToColorSpace(destinationColorSpace);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> TextStream& FilterEffect::externalRepresentation(TextStream& ts, RepresentationType representationType) const
</span><span class="lines">@@ -643,8 +234,8 @@
</span><span class="cx">     if (representationType == RepresentationType::Debugging) {
</span><span class="cx">         TextStream::IndentScope indentScope(ts);
</span><span class="cx">         ts.dumpProperty("alpha image", m_alphaImage);
</span><del>-        ts.dumpProperty("operating colorspace", m_operatingColorSpace);
-        ts.dumpProperty("result colorspace", m_resultColorSpace);
</del><ins>+        ts.dumpProperty("operating colorspace", operatingColorSpace());
+        ts.dumpProperty("result colorspace", resultColorSpace());
</ins><span class="cx">         ts << "\n" << indent;
</span><span class="cx">     }
</span><span class="cx">     return ts;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFilterEffecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/FilterEffect.h (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -22,17 +22,11 @@
</span><span class="cx"> 
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><del>-#include "AlphaPremultiplication.h"
</del><span class="cx"> #include "DestinationColorSpace.h"
</span><span class="cx"> #include "FilterEffectVector.h"
</span><span class="cx"> #include "FilterFunction.h"
</span><del>-#include "FloatRect.h"
</del><ins>+#include "FilterImage.h"
</ins><span class="cx"> #include "IntRect.h"
</span><del>-#include "IntRectExtent.h"
-#include "PixelBuffer.h"
-#include <JavaScriptCore/Forward.h>
-#include <wtf/MathExtras.h>
-#include <wtf/RefPtr.h>
</del><span class="cx"> #include <wtf/Vector.h>
</span><span class="cx"> 
</span><span class="cx"> namespace WTF {
</span><span class="lines">@@ -46,26 +40,22 @@
</span><span class="cx"> 
</span><span class="cx"> class FilterEffect : public FilterFunction {
</span><span class="cx"> public:
</span><ins>+    bool createResult();
</ins><span class="cx">     void clearResult() override;
</span><span class="cx">     void clearResultsRecursive();
</span><span class="cx"> 
</span><ins>+    bool hasResult() const { return m_filterImage; }
+
</ins><span class="cx">     ImageBuffer* imageBufferResult();
</span><del>-    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);
</del><ins>+    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();
+
</ins><span class="cx">     FilterEffectVector& inputEffects() { return m_inputEffects; }
</span><span class="cx">     FilterEffect* inputEffect(unsigned) const;
</span><span class="cx">     unsigned numberOfEffectInputs() const { return m_inputEffects.size(); }
</span><span class="cx">     
</span><del>-    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;
-    }
-
</del><span class="cx">     FloatRect drawingRegionOfInputImage(const IntRect&) const;
</span><span class="cx">     IntRect requestedRegionOfInputPixelBuffer(const IntRect&) const;
</span><span class="cx">     
</span><span class="lines">@@ -123,9 +113,9 @@
</span><span class="cx"> 
</span><span class="cx">     const DestinationColorSpace& operatingColorSpace() const { return m_operatingColorSpace; }
</span><span class="cx">     virtual void setOperatingColorSpace(const DestinationColorSpace& colorSpace) { m_operatingColorSpace = colorSpace; }
</span><del>-    const DestinationColorSpace& resultColorSpace() const { return m_resultColorSpace; }
-    virtual void setResultColorSpace(const DestinationColorSpace& colorSpace) { m_resultColorSpace = colorSpace; }
</del><span class="cx"> 
</span><ins>+    virtual const DestinationColorSpace& resultColorSpace() const { return m_operatingColorSpace; }
+
</ins><span class="cx">     virtual void transformResultColorSpace(FilterEffect* in, const int) { in->transformResultColorSpace(m_operatingColorSpace); }
</span><span class="cx">     void transformResultColorSpace(const DestinationColorSpace&);
</span><span class="cx">     
</span><span class="lines">@@ -138,36 +128,18 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx"> protected:
</span><del>-    FilterEffect(Type);
-    
-    ImageBuffer* createImageBufferResult();
-    std::optional<PixelBuffer>& createUnmultipliedImageResult();
-    std::optional<PixelBuffer>& createPremultipliedImageResult();
</del><ins>+    using FilterFunction::FilterFunction;
</ins><span class="cx"> 
</span><del>-    // 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; }
</del><ins>+    virtual bool mayProduceInvalidPremultipliedPixels() const { return false; }
</ins><span class="cx"> 
</span><del>-    // If a pre-multiplied image, check every pixel for validity and correct if necessary.
-    void forceValidPreMultipliedPixels();
-
</del><span class="cx">     void clipAbsolutePaintRect();
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     virtual bool platformApplySoftware(const Filter&) = 0;
</span><span class="cx"> 
</span><del>-    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&);
-    
</del><span class="cx">     FilterEffectVector m_inputEffects;
</span><span class="cx"> 
</span><del>-    RefPtr<ImageBuffer> m_imageBufferResult;
-    std::optional<PixelBuffer> m_unmultipliedImageResult;
-    std::optional<PixelBuffer> m_premultipliedImageResult;
</del><ins>+    RefPtr<FilterImage> m_filterImage;
</ins><span class="cx"> 
</span><span class="cx">     IntRect m_absolutePaintRect;
</span><span class="cx">     
</span><span class="lines">@@ -195,12 +167,7 @@
</span><span class="cx">     // Should the effect clip to its primitive region, or expand to use the combined region of its inputs.
</span><span class="cx">     bool m_clipsToBounds { true };
</span><span class="cx"> 
</span><del>-#if ENABLE(DESTINATION_COLOR_SPACE_LINEAR_SRGB)
-    DestinationColorSpace m_operatingColorSpace { DestinationColorSpace::LinearSRGB() };
-#else
</del><span class="cx">     DestinationColorSpace m_operatingColorSpace { DestinationColorSpace::SRGB() };
</span><del>-#endif
-    DestinationColorSpace m_resultColorSpace { DestinationColorSpace::SRGB() };
</del><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, const FilterEffect&);
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFilterImagecpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/platform/graphics/filters/FilterImage.cpp (0 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,322 @@
</span><ins>+/*
+ * 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
</ins></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersFilterImageh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/platform/graphics/filters/FilterImage.h (0 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,74 @@
</span><ins>+/*
+ * 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 <JavaScriptCore/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
</ins></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersSourceAlphacpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/SourceAlpha.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -47,7 +47,7 @@
</span><span class="cx"> 
</span><span class="cx"> bool SourceAlpha::platformApplySoftware(const Filter&)
</span><span class="cx"> {
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
</del><ins>+    ImageBuffer* resultImage = imageBufferResult();
</ins><span class="cx">     if (!resultImage)
</span><span class="cx">         return false;
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfiltersSourceGraphiccpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/filters/SourceGraphic.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -34,7 +34,6 @@
</span><span class="cx"> SourceGraphic::SourceGraphic()
</span><span class="cx">     : FilterEffect(FilterEffect::Type::SourceGraphic)
</span><span class="cx"> {
</span><del>-    setOperatingColorSpace(DestinationColorSpace::SRGB());
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void SourceGraphic::determineAbsolutePaintRect(const Filter& filter)
</span><span class="lines">@@ -45,7 +44,7 @@
</span><span class="cx"> 
</span><span class="cx"> bool SourceGraphic::platformApplySoftware(const Filter& filter)
</span><span class="cx"> {
</span><del>-    ImageBuffer* resultImage = createImageBufferResult();
</del><ins>+    ImageBuffer* resultImage = imageBufferResult();
</ins><span class="cx">     ImageBuffer* sourceImage = filter.sourceImage();
</span><span class="cx">     if (!resultImage || !sourceImage)
</span><span class="cx">         return false;
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingsvgRenderSVGResourceFiltercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/svg/RenderSVGResourceFilter.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -241,7 +241,7 @@
</span><span class="cx">         if (!lastEffect->hasResult()) {
</span><span class="cx">             filterData.state = FilterData::Applying;
</span><span class="cx">             filterData.filter->apply();
</span><del>-            lastEffect->correctFilterResultIfNeeded();
</del><ins>+            lastEffect->correctPremultipliedResultIfNeeded();
</ins><span class="cx">             lastEffect->transformResultColorSpace(DestinationColorSpace::SRGB());
</span><span class="cx">         }
</span><span class="cx">         filterData.state = FilterData::Built;
</span></span></pre></div>
<a id="trunkSourceWebCoresvggraphicsfiltersSVGFEImagecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.cpp (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -76,10 +76,7 @@
</span><span class="cx"> 
</span><span class="cx"> bool FEImage::platformApplySoftware(const Filter& filter)
</span><span class="cx"> {
</span><del>-    // FEImage results are always in DestinationColorSpace::SRGB()
-    setResultColorSpace(DestinationColorSpace::SRGB());
-
-    ImageBuffer* resultImage = createImageBufferResult();
</del><ins>+    auto resultImage = imageBufferResult();
</ins><span class="cx">     if (!resultImage)
</span><span class="cx">         return false;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoresvggraphicsfiltersSVGFEImageh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/graphics/filters/SVGFEImage.h (286128 => 286129)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -44,6 +44,9 @@
</span><span class="cx"> private:
</span><span class="cx">     FEImage(SourceImage&&, const FloatRect& sourceImageRect, const SVGPreserveAspectRatioValue&);
</span><span class="cx"> 
</span><ins>+    // FEImage results are always in DestinationColorSpace::SRGB()
+    void setOperatingColorSpace(const DestinationColorSpace&) override { }
+
</ins><span class="cx">     void determineAbsolutePaintRect(const Filter&) final;
</span><span class="cx"> 
</span><span class="cx">     bool platformApplySoftware(const Filter&) final;
</span></span></pre>
</div>
</div>

</body>
</html>