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 | |
41 | namespace WebCore { |
42 | |
43 | RenderLayerFilters::RenderLayerFilters(RenderLayer& layer) |
44 | : m_layer(layer) |
45 | { |
46 | } |
47 | |
48 | RenderLayerFilters::~RenderLayerFilters() |
49 | { |
50 | removeReferenceFilterClients(); |
51 | } |
52 | |
53 | void RenderLayerFilters::setFilter(RefPtr<CSSFilter>&& filter) |
54 | { |
55 | m_filter = WTFMove(filter); |
56 | } |
57 | |
58 | bool RenderLayerFilters::hasFilterThatMovesPixels() const |
59 | { |
60 | return m_filter && m_filter->hasFilterThatMovesPixels(); |
61 | } |
62 | |
63 | bool RenderLayerFilters::hasFilterThatShouldBeRestrictedBySecurityOrigin() const |
64 | { |
65 | return m_filter && m_filter->hasFilterThatShouldBeRestrictedBySecurityOrigin(); |
66 | } |
67 | |
68 | void RenderLayerFilters::notifyFinished(CachedResource&) |
69 | { |
70 | m_layer.filterNeedsRepaint(); |
71 | } |
72 | |
73 | void 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 | |
101 | void 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 | |
115 | void 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 | |
133 | GraphicsContext* 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 | |
174 | void 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 | |