1/*
2 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
3 * Copyright (C) 2013 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "RenderLayerFilters.h"
33
34#include "CSSFilter.h"
35#include "CachedSVGDocument.h"
36#include "CachedSVGDocumentReference.h"
37#include "Logging.h"
38#include "RenderSVGResourceFilter.h"
39#include <wtf/NeverDestroyed.h>
40
41namespace WebCore {
42
43RenderLayerFilters::RenderLayerFilters(RenderLayer& layer)
44 : m_layer(layer)
45{
46}
47
48RenderLayerFilters::~RenderLayerFilters()
49{
50 removeReferenceFilterClients();
51}
52
53void RenderLayerFilters::setFilter(RefPtr<CSSFilter>&& filter)
54{
55 m_filter = WTFMove(filter);
56}
57
58bool RenderLayerFilters::hasFilterThatMovesPixels() const
59{
60 return m_filter && m_filter->hasFilterThatMovesPixels();
61}
62
63bool RenderLayerFilters::hasFilterThatShouldBeRestrictedBySecurityOrigin() const
64{
65 return m_filter && m_filter->hasFilterThatShouldBeRestrictedBySecurityOrigin();
66}
67
68void RenderLayerFilters::notifyFinished(CachedResource&)
69{
70 m_layer.filterNeedsRepaint();
71}
72
73void RenderLayerFilters::updateReferenceFilterClients(const FilterOperations& operations)
74{
75 removeReferenceFilterClients();
76
77 for (auto& operation : operations.operations()) {
78 if (!is<ReferenceFilterOperation>(*operation))
79 continue;
80
81 auto& referenceOperation = downcast<ReferenceFilterOperation>(*operation);
82 auto* documentReference = referenceOperation.cachedSVGDocumentReference();
83 if (auto* cachedSVGDocument = documentReference ? documentReference->document() : nullptr) {
84 // Reference is external; wait for notifyFinished().
85 cachedSVGDocument->addClient(*this);
86 m_externalSVGReferences.append(cachedSVGDocument);
87 } else {
88 // Reference is internal; add layer as a client so we can trigger filter repaint on SVG attribute change.
89 auto* filterElement = m_layer.renderer().document().getElementById(referenceOperation.fragment());
90 if (!filterElement)
91 continue;
92 auto* renderer = filterElement->renderer();
93 if (!is<RenderSVGResourceFilter>(renderer))
94 continue;
95 downcast<RenderSVGResourceFilter>(*renderer).addClientRenderLayer(&m_layer);
96 m_internalSVGReferences.append(filterElement);
97 }
98 }
99}
100
101void RenderLayerFilters::removeReferenceFilterClients()
102{
103 for (auto& resourceHandle : m_externalSVGReferences)
104 resourceHandle->removeClient(*this);
105
106 m_externalSVGReferences.clear();
107
108 for (auto& filterElement : m_internalSVGReferences) {
109 if (auto* renderer = filterElement->renderer())
110 downcast<RenderSVGResourceContainer>(*renderer).removeClientRenderLayer(&m_layer);
111 }
112 m_internalSVGReferences.clear();
113}
114
115void RenderLayerFilters::buildFilter(RenderElement& renderer, float scaleFactor, RenderingMode renderingMode)
116{
117 if (!m_filter) {
118 m_filter = CSSFilter::create();
119 m_filter->setFilterScale(scaleFactor);
120 m_filter->setRenderingMode(renderingMode);
121 } else if (m_filter->filterScale() != scaleFactor) {
122 m_filter->setFilterScale(scaleFactor);
123 m_filter->clearIntermediateResults();
124 }
125
126 // If the filter fails to build, remove it from the layer. It will still attempt to
127 // go through regular processing (e.g. compositing), but never apply anything.
128 // FIXME: this rebuilds the entire effects chain even if the filter style didn't change.
129 if (!m_filter->build(renderer, renderer.style().filter(), FilterConsumer::FilterProperty))
130 m_filter = nullptr;
131}
132
133GraphicsContext* RenderLayerFilters::beginFilterEffect(GraphicsContext& destinationContext, const LayoutRect& filterBoxRect, const LayoutRect& dirtyRect, const LayoutRect& layerRepaintRect)
134{
135 if (!m_filter)
136 return nullptr;
137
138 auto& filter = *m_filter;
139 auto filterSourceRect = filter.computeSourceImageRectForDirtyRect(filterBoxRect, dirtyRect);
140 if (filterSourceRect.isEmpty())
141 return nullptr;
142
143 bool hasUpdatedBackingStore = filter.updateBackingStoreRect(filterSourceRect);
144 if (!filter.hasFilterThatMovesPixels())
145 m_repaintRect = dirtyRect;
146 else {
147 if (hasUpdatedBackingStore)
148 m_repaintRect = filterSourceRect;
149 else {
150 m_repaintRect = dirtyRect;
151 m_repaintRect.unite(layerRepaintRect);
152 m_repaintRect.intersect(filterSourceRect);
153 }
154 }
155 m_paintOffset = filterSourceRect.location();
156 resetDirtySourceRect();
157
158 filter.determineFilterPrimitiveSubregion();
159
160 filter.allocateBackingStoreIfNeeded(destinationContext);
161 auto* sourceGraphicsContext = filter.inputContext();
162 if (!sourceGraphicsContext || filter.filterRegion().isEmpty() || ImageBuffer::sizeNeedsClamping(filter.filterRegion().size()))
163 return nullptr;
164
165 // Translate the context so that the contents of the layer is captured in the offscreen memory buffer.
166 sourceGraphicsContext->save();
167 sourceGraphicsContext->translate(-m_paintOffset);
168 sourceGraphicsContext->clearRect(m_repaintRect);
169 sourceGraphicsContext->clip(m_repaintRect);
170
171 return sourceGraphicsContext;
172}
173
174void RenderLayerFilters::applyFilterEffect(GraphicsContext& destinationContext)
175{
176 ASSERT(m_filter->inputContext());
177
178 LOG_WITH_STREAM(Filters, stream << "\nRenderLayerFilters " << this << " applyFilterEffect");
179
180 auto& filter = *m_filter;
181 filter.inputContext()->restore();
182
183 filter.apply();
184
185 // Get the filtered output and draw it in place.
186 LayoutRect destRect = filter.outputRect();
187 destRect.move(m_paintOffset.x(), m_paintOffset.y());
188
189 if (auto* outputBuffer = filter.output())
190 destinationContext.drawImageBuffer(*outputBuffer, snapRectToDevicePixels(destRect, m_layer.renderer().document().deviceScaleFactor()));
191
192 filter.clearIntermediateResults();
193
194 LOG_WITH_STREAM(Filters, stream << "RenderLayerFilters " << this << " applyFilterEffect done\n");
195}
196
197} // namespace WebCore
198