1/*
2 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
17 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
18 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
22 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
23 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "CSSFilterImageValue.h"
29
30#include "CSSFilter.h"
31#include "CachedImage.h"
32#include "CachedResourceLoader.h"
33#include "GraphicsContext.h"
34#include "ImageBuffer.h"
35#include "RenderElement.h"
36#include "StyleCachedImage.h"
37#include "StyleResolver.h"
38#include <wtf/text/StringBuilder.h>
39
40namespace WebCore {
41
42CSSFilterImageValue::~CSSFilterImageValue()
43{
44 if (m_cachedImage)
45 m_cachedImage->removeClient(m_filterSubimageObserver);
46}
47
48String CSSFilterImageValue::customCSSText() const
49{
50 StringBuilder result;
51 result.appendLiteral("filter(");
52 result.append(m_imageValue->cssText());
53 result.appendLiteral(", ");
54 result.append(m_filterValue->cssText());
55 result.append(')');
56 return result.toString();
57}
58
59FloatSize CSSFilterImageValue::fixedSize(const RenderElement* renderer)
60{
61 // FIXME: Skip Content Security Policy check when filter is applied to an element in a user agent shadow tree.
62 // See <https://bugs.webkit.org/show_bug.cgi?id=146663>.
63 ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
64
65 CachedResourceLoader& cachedResourceLoader = renderer->document().cachedResourceLoader();
66 CachedImage* cachedImage = cachedImageForCSSValue(m_imageValue, cachedResourceLoader, options);
67
68 if (!cachedImage)
69 return FloatSize();
70
71 return cachedImage->imageForRenderer(renderer)->size();
72}
73
74bool CSSFilterImageValue::isPending() const
75{
76 return CSSImageGeneratorValue::subimageIsPending(m_imageValue);
77}
78
79bool CSSFilterImageValue::knownToBeOpaque(const RenderElement&) const
80{
81 return false;
82}
83
84void CSSFilterImageValue::loadSubimages(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
85{
86 CachedResourceHandle<CachedImage> oldCachedImage = m_cachedImage;
87
88 m_cachedImage = CSSImageGeneratorValue::cachedImageForCSSValue(m_imageValue, cachedResourceLoader, options);
89
90 if (m_cachedImage != oldCachedImage) {
91 if (oldCachedImage)
92 oldCachedImage->removeClient(m_filterSubimageObserver);
93 if (m_cachedImage)
94 m_cachedImage->addClient(m_filterSubimageObserver);
95 }
96
97 for (auto& filterOperation : m_filterOperations.operations()) {
98 if (!is<ReferenceFilterOperation>(filterOperation))
99 continue;
100 auto& referenceFilterOperation = downcast<ReferenceFilterOperation>(*filterOperation);
101 referenceFilterOperation.loadExternalDocumentIfNeeded(cachedResourceLoader, options);
102 }
103
104 m_filterSubimageObserver.setReady(true);
105}
106
107RefPtr<Image> CSSFilterImageValue::image(RenderElement* renderer, const FloatSize& size)
108{
109 ASSERT(renderer);
110
111 if (size.isEmpty())
112 return nullptr;
113
114 // FIXME: Skip Content Security Policy check when filter is applied to an element in a user agent shadow tree.
115 // See <https://bugs.webkit.org/show_bug.cgi?id=146663>.
116 ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
117 auto* cachedImage = cachedImageForCSSValue(m_imageValue, renderer->document().cachedResourceLoader(), options);
118 if (!cachedImage)
119 return &Image::nullImage();
120
121 auto* image = cachedImage->imageForRenderer(renderer);
122 if (!image)
123 return &Image::nullImage();
124
125 // Transform Image into ImageBuffer.
126 // FIXME (149424): This buffer should not be unconditionally unaccelerated.
127 auto texture = ImageBuffer::create(size, Unaccelerated);
128 if (!texture)
129 return &Image::nullImage();
130
131 auto imageRect = FloatRect { { }, size };
132 texture->context().drawImage(*image, imageRect);
133
134 auto cssFilter = CSSFilter::create();
135 cssFilter->setSourceImage(WTFMove(texture));
136 cssFilter->setSourceImageRect(imageRect);
137 cssFilter->setFilterRegion(imageRect);
138 if (!cssFilter->build(*renderer, m_filterOperations, FilterConsumer::FilterFunction))
139 return &Image::nullImage();
140 cssFilter->apply();
141
142 return cssFilter->output()->copyImage();
143}
144
145void CSSFilterImageValue::filterImageChanged(const IntRect&)
146{
147 for (auto& client : clients())
148 client.key->imageChanged(static_cast<WrappedImagePtr>(this));
149}
150
151void CSSFilterImageValue::createFilterOperations(StyleResolver* resolver)
152{
153 m_filterOperations.clear();
154 resolver->createFilterOperations(m_filterValue, m_filterOperations);
155}
156
157void CSSFilterImageValue::FilterSubimageObserverProxy::imageChanged(CachedImage*, const IntRect* rect)
158{
159 if (m_ready)
160 m_ownerValue->filterImageChanged(*rect);
161}
162
163bool CSSFilterImageValue::traverseSubresources(const WTF::Function<bool (const CachedResource&)>& handler) const
164{
165 if (!m_cachedImage)
166 return false;
167 return handler(*m_cachedImage);
168}
169
170bool CSSFilterImageValue::equals(const CSSFilterImageValue& other) const
171{
172 return equalInputImages(other) && compareCSSValue(m_filterValue, other.m_filterValue);
173}
174
175bool CSSFilterImageValue::equalInputImages(const CSSFilterImageValue& other) const
176{
177 return compareCSSValue(m_imageValue, other.m_imageValue);
178}
179
180} // namespace WebCore
181