1/*
2 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
3 * Copyright (C) 2018-2019 Apple Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "SVGFEConvolveMatrixElement.h"
23
24#include "FilterEffect.h"
25#include "FloatPoint.h"
26#include "IntPoint.h"
27#include "IntSize.h"
28#include "SVGFilterBuilder.h"
29#include "SVGNames.h"
30#include "SVGParserUtilities.h"
31#include <wtf/IsoMallocInlines.h>
32
33namespace WebCore {
34
35WTF_MAKE_ISO_ALLOCATED_IMPL(SVGFEConvolveMatrixElement);
36
37inline SVGFEConvolveMatrixElement::SVGFEConvolveMatrixElement(const QualifiedName& tagName, Document& document)
38 : SVGFilterPrimitiveStandardAttributes(tagName, document)
39{
40 ASSERT(hasTagName(SVGNames::feConvolveMatrixTag));
41
42 static std::once_flag onceFlag;
43 std::call_once(onceFlag, [] {
44 PropertyRegistry::registerProperty<SVGNames::inAttr, &SVGFEConvolveMatrixElement::m_in1>();
45 PropertyRegistry::registerProperty<SVGNames::orderAttr, &SVGFEConvolveMatrixElement::m_orderX, &SVGFEConvolveMatrixElement::m_orderY>();
46 PropertyRegistry::registerProperty<SVGNames::kernelMatrixAttr, &SVGFEConvolveMatrixElement::m_kernelMatrix>();
47 PropertyRegistry::registerProperty<SVGNames::divisorAttr, &SVGFEConvolveMatrixElement::m_divisor>();
48 PropertyRegistry::registerProperty<SVGNames::biasAttr, &SVGFEConvolveMatrixElement::m_bias>();
49 PropertyRegistry::registerProperty<SVGNames::targetXAttr, &SVGFEConvolveMatrixElement::m_targetX>();
50 PropertyRegistry::registerProperty<SVGNames::targetYAttr, &SVGFEConvolveMatrixElement::m_targetY>();
51 PropertyRegistry::registerProperty<SVGNames::edgeModeAttr, EdgeModeType, &SVGFEConvolveMatrixElement::m_edgeMode>();
52 PropertyRegistry::registerProperty<SVGNames::kernelUnitLengthAttr, &SVGFEConvolveMatrixElement::m_kernelUnitLengthX, &SVGFEConvolveMatrixElement::m_kernelUnitLengthY>();
53 PropertyRegistry::registerProperty<SVGNames::preserveAlphaAttr, &SVGFEConvolveMatrixElement::m_preserveAlpha>();
54 });
55}
56
57Ref<SVGFEConvolveMatrixElement> SVGFEConvolveMatrixElement::create(const QualifiedName& tagName, Document& document)
58{
59 return adoptRef(*new SVGFEConvolveMatrixElement(tagName, document));
60}
61
62void SVGFEConvolveMatrixElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
63{
64 if (name == SVGNames::inAttr) {
65 m_in1->setBaseValInternal(value);
66 return;
67 }
68
69 if (name == SVGNames::orderAttr) {
70 float x, y;
71 if (parseNumberOptionalNumber(value, x, y) && x >= 1 && y >= 1) {
72 m_orderX->setBaseValInternal(x);
73 m_orderY->setBaseValInternal(y);
74 } else
75 document().accessSVGExtensions().reportWarning("feConvolveMatrix: problem parsing order=\"" + value + "\". Filtered element will not be displayed.");
76 return;
77 }
78
79 if (name == SVGNames::edgeModeAttr) {
80 EdgeModeType propertyValue = SVGPropertyTraits<EdgeModeType>::fromString(value);
81 if (propertyValue > 0)
82 m_edgeMode->setBaseValInternal<EdgeModeType>(propertyValue);
83 else
84 document().accessSVGExtensions().reportWarning("feConvolveMatrix: problem parsing edgeMode=\"" + value + "\". Filtered element will not be displayed.");
85 return;
86 }
87
88 if (name == SVGNames::kernelMatrixAttr) {
89 m_kernelMatrix->baseVal()->parse(value);
90 return;
91 }
92
93 if (name == SVGNames::divisorAttr) {
94 float divisor = value.toFloat();
95 if (divisor)
96 m_divisor->setBaseValInternal(divisor);
97 else
98 document().accessSVGExtensions().reportWarning("feConvolveMatrix: problem parsing divisor=\"" + value + "\". Filtered element will not be displayed.");
99 return;
100 }
101
102 if (name == SVGNames::biasAttr) {
103 m_bias->setBaseValInternal(value.toFloat());
104 return;
105 }
106
107 if (name == SVGNames::targetXAttr) {
108 m_targetX->setBaseValInternal(value.string().toUIntStrict());
109 return;
110 }
111
112 if (name == SVGNames::targetYAttr) {
113 m_targetY->setBaseValInternal(value.string().toUIntStrict());
114 return;
115 }
116
117 if (name == SVGNames::kernelUnitLengthAttr) {
118 float x, y;
119 if (parseNumberOptionalNumber(value, x, y) && x > 0 && y > 0) {
120 m_kernelUnitLengthX->setBaseValInternal(x);
121 m_kernelUnitLengthY->setBaseValInternal(y);
122 } else
123 document().accessSVGExtensions().reportWarning("feConvolveMatrix: problem parsing kernelUnitLength=\"" + value + "\". Filtered element will not be displayed.");
124 return;
125 }
126
127 if (name == SVGNames::preserveAlphaAttr) {
128 if (value == "true")
129 m_preserveAlpha->setBaseValInternal(true);
130 else if (value == "false")
131 m_preserveAlpha->setBaseValInternal(false);
132 else
133 document().accessSVGExtensions().reportWarning("feConvolveMatrix: problem parsing preserveAlphaAttr=\"" + value + "\". Filtered element will not be displayed.");
134 return;
135 }
136
137 SVGFilterPrimitiveStandardAttributes::parseAttribute(name, value);
138}
139
140bool SVGFEConvolveMatrixElement::setFilterEffectAttribute(FilterEffect* effect, const QualifiedName& attrName)
141{
142 FEConvolveMatrix* convolveMatrix = static_cast<FEConvolveMatrix*>(effect);
143 if (attrName == SVGNames::edgeModeAttr)
144 return convolveMatrix->setEdgeMode(edgeMode());
145 if (attrName == SVGNames::divisorAttr)
146 return convolveMatrix->setDivisor(divisor());
147 if (attrName == SVGNames::biasAttr)
148 return convolveMatrix->setBias(bias());
149 if (attrName == SVGNames::targetXAttr)
150 return convolveMatrix->setTargetOffset(IntPoint(targetX(), targetY()));
151 if (attrName == SVGNames::targetYAttr)
152 return convolveMatrix->setTargetOffset(IntPoint(targetX(), targetY()));
153 if (attrName == SVGNames::kernelUnitLengthAttr)
154 return convolveMatrix->setKernelUnitLength(FloatPoint(kernelUnitLengthX(), kernelUnitLengthY()));
155 if (attrName == SVGNames::preserveAlphaAttr)
156 return convolveMatrix->setPreserveAlpha(preserveAlpha());
157
158 ASSERT_NOT_REACHED();
159 return false;
160}
161
162void SVGFEConvolveMatrixElement::setOrder(float x, float y)
163{
164 m_orderX->setBaseValInternal(x);
165 m_orderY->setBaseValInternal(y);
166 invalidate();
167}
168
169void SVGFEConvolveMatrixElement::setKernelUnitLength(float x, float y)
170{
171 m_kernelUnitLengthX->setBaseValInternal(x);
172 m_kernelUnitLengthY->setBaseValInternal(y);
173 invalidate();
174}
175
176void SVGFEConvolveMatrixElement::svgAttributeChanged(const QualifiedName& attrName)
177{
178 if (attrName == SVGNames::edgeModeAttr || attrName == SVGNames::divisorAttr || attrName == SVGNames::biasAttr || attrName == SVGNames::targetXAttr || attrName == SVGNames::targetYAttr || attrName == SVGNames::kernelUnitLengthAttr || attrName == SVGNames::preserveAlphaAttr) {
179 InstanceInvalidationGuard guard(*this);
180 primitiveAttributeChanged(attrName);
181 return;
182 }
183
184 if (attrName == SVGNames::inAttr || attrName == SVGNames::orderAttr || attrName == SVGNames::kernelMatrixAttr) {
185 InstanceInvalidationGuard guard(*this);
186 invalidate();
187 return;
188 }
189
190 SVGFilterPrimitiveStandardAttributes::svgAttributeChanged(attrName);
191}
192
193RefPtr<FilterEffect> SVGFEConvolveMatrixElement::build(SVGFilterBuilder* filterBuilder, Filter& filter) const
194{
195 auto input1 = filterBuilder->getEffectById(in1());
196
197 if (!input1)
198 return nullptr;
199
200 int orderXValue = orderX();
201 int orderYValue = orderY();
202 if (!hasAttribute(SVGNames::orderAttr)) {
203 orderXValue = 3;
204 orderYValue = 3;
205 }
206 // Spec says order must be > 0. Bail if it is not.
207 if (orderXValue < 1 || orderYValue < 1)
208 return nullptr;
209 auto& kernelMatrix = this->kernelMatrix();
210 int kernelMatrixSize = kernelMatrix.items().size();
211 // The spec says this is a requirement, and should bail out if fails
212 if (orderXValue * orderYValue != kernelMatrixSize)
213 return nullptr;
214
215 int targetXValue = targetX();
216 int targetYValue = targetY();
217 if (hasAttribute(SVGNames::targetXAttr) && (targetXValue < 0 || targetXValue >= orderXValue))
218 return nullptr;
219 // The spec says the default value is: targetX = floor ( orderX / 2 ))
220 if (!hasAttribute(SVGNames::targetXAttr))
221 targetXValue = static_cast<int>(floorf(orderXValue / 2));
222 if (hasAttribute(SVGNames::targetYAttr) && (targetYValue < 0 || targetYValue >= orderYValue))
223 return nullptr;
224 // The spec says the default value is: targetY = floor ( orderY / 2 ))
225 if (!hasAttribute(SVGNames::targetYAttr))
226 targetYValue = static_cast<int>(floorf(orderYValue / 2));
227
228 // Spec says default kernelUnitLength is 1.0, and a specified length cannot be 0.
229 int kernelUnitLengthXValue = kernelUnitLengthX();
230 int kernelUnitLengthYValue = kernelUnitLengthY();
231 if (!hasAttribute(SVGNames::kernelUnitLengthAttr)) {
232 kernelUnitLengthXValue = 1;
233 kernelUnitLengthYValue = 1;
234 }
235 if (kernelUnitLengthXValue <= 0 || kernelUnitLengthYValue <= 0)
236 return nullptr;
237
238 float divisorValue = divisor();
239 if (hasAttribute(SVGNames::divisorAttr) && !divisorValue)
240 return nullptr;
241 if (!hasAttribute(SVGNames::divisorAttr)) {
242 for (int i = 0; i < kernelMatrixSize; ++i)
243 divisorValue += kernelMatrix.items()[i]->value();
244 if (!divisorValue)
245 divisorValue = 1;
246 }
247
248 auto effect = FEConvolveMatrix::create(filter, IntSize(orderXValue, orderYValue), divisorValue, bias(), IntPoint(targetXValue, targetYValue), edgeMode(), FloatPoint(kernelUnitLengthXValue, kernelUnitLengthYValue), preserveAlpha(), kernelMatrix);
249 effect->inputEffects().append(input1);
250 return effect;
251}
252
253} // namespace WebCore
254