Extended Color: ColorMatrix should support smaller matrices and be constexpr
[WebKit-https.git] / Source / WebCore / platform / graphics / filters / FilterOperation.cpp
1 /*
2  * Copyright (C) 2011 Apple Inc. 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 APPLE INC. ``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 APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, 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 THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "FilterOperation.h"
28
29 #include "AnimationUtilities.h"
30 #include "CachedResourceLoader.h"
31 #include "CachedSVGDocumentReference.h"
32 #include "ColorMatrix.h"
33 #include "ColorUtilities.h"
34 #include "FilterEffect.h"
35 #include "SVGURIReference.h"
36 #include <wtf/text/TextStream.h>
37
38 namespace WebCore {
39     
40 bool DefaultFilterOperation::operator==(const FilterOperation& operation) const
41 {
42     if (!isSameType(operation))
43         return false;
44     
45     return representedType() == downcast<DefaultFilterOperation>(operation).representedType();
46 }
47
48 ReferenceFilterOperation::ReferenceFilterOperation(const String& url, const String& fragment)
49     : FilterOperation(REFERENCE)
50     , m_url(url)
51     , m_fragment(fragment)
52 {
53 }
54
55 ReferenceFilterOperation::~ReferenceFilterOperation() = default;
56     
57 bool ReferenceFilterOperation::operator==(const FilterOperation& operation) const
58 {
59     if (!isSameType(operation))
60         return false;
61     
62     return m_url == downcast<ReferenceFilterOperation>(operation).m_url;
63 }
64
65 void ReferenceFilterOperation::loadExternalDocumentIfNeeded(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
66 {
67     if (m_cachedSVGDocumentReference)
68         return;
69     if (!SVGURIReference::isExternalURIReference(m_url, *cachedResourceLoader.document()))
70         return;
71     m_cachedSVGDocumentReference = makeUnique<CachedSVGDocumentReference>(m_url);
72     m_cachedSVGDocumentReference->load(cachedResourceLoader, options);
73 }
74
75 RefPtr<FilterOperation> BasicColorMatrixFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
76 {
77     if (from && !from->isSameType(*this))
78         return this;
79     
80     if (blendToPassthrough)
81         return BasicColorMatrixFilterOperation::create(WebCore::blend(m_amount, passthroughAmount(), progress), m_type);
82         
83     const BasicColorMatrixFilterOperation* fromOperation = downcast<BasicColorMatrixFilterOperation>(from);
84     double fromAmount = fromOperation ? fromOperation->amount() : passthroughAmount();
85     return BasicColorMatrixFilterOperation::create(WebCore::blend(fromAmount, m_amount, progress), m_type);
86 }
87
88 bool BasicColorMatrixFilterOperation::transformColor(ColorComponents<float>& colorComponents) const
89 {
90     switch (m_type) {
91     case GRAYSCALE: {
92         auto matrix = grayscaleColorMatrix(m_amount);
93         matrix.transformColorComponents(colorComponents);
94         return true;
95     }
96     case SEPIA: {
97         auto matrix = sepiaColorMatrix(m_amount);
98         matrix.transformColorComponents(colorComponents);
99         return true;
100     }
101     case HUE_ROTATE: {
102         auto matrix = hueRotateColorMatrix(m_amount);
103         matrix.transformColorComponents(colorComponents);
104         return true;
105     }
106     case SATURATE: {
107         auto matrix = saturationColorMatrix(m_amount);
108         matrix.transformColorComponents(colorComponents);
109         return true;
110     }
111     default:
112         ASSERT_NOT_REACHED();
113         return false;
114     }
115
116     return false;
117 }
118
119 inline bool BasicColorMatrixFilterOperation::operator==(const FilterOperation& operation) const
120 {
121     if (!isSameType(operation))
122         return false;
123     const BasicColorMatrixFilterOperation& other = downcast<BasicColorMatrixFilterOperation>(operation);
124     return m_amount == other.m_amount;
125 }
126
127 double BasicColorMatrixFilterOperation::passthroughAmount() const
128 {
129     switch (m_type) {
130     case GRAYSCALE:
131     case SEPIA:
132     case HUE_ROTATE:
133         return 0;
134     case SATURATE:
135         return 1;
136     default:
137         ASSERT_NOT_REACHED();
138         return 0;
139     }
140 }
141
142 RefPtr<FilterOperation> BasicComponentTransferFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
143 {
144     if (from && !from->isSameType(*this))
145         return this;
146     
147     if (blendToPassthrough)
148         return BasicComponentTransferFilterOperation::create(WebCore::blend(m_amount, passthroughAmount(), progress), m_type);
149         
150     const BasicComponentTransferFilterOperation* fromOperation = downcast<BasicComponentTransferFilterOperation>(from);
151     double fromAmount = fromOperation ? fromOperation->amount() : passthroughAmount();
152     return BasicComponentTransferFilterOperation::create(WebCore::blend(fromAmount, m_amount, progress), m_type);
153 }
154
155 bool BasicComponentTransferFilterOperation::transformColor(ColorComponents<float>& colorComponents) const
156 {
157     switch (m_type) {
158     case OPACITY:
159         colorComponents.components[3] *= m_amount;
160         return true;
161     case INVERT: {
162         float oneMinusAmount = 1.f - m_amount;
163         colorComponents.components[0] = 1 - (oneMinusAmount + colorComponents.components[0] * (m_amount - oneMinusAmount));
164         colorComponents.components[1] = 1 - (oneMinusAmount + colorComponents.components[1] * (m_amount - oneMinusAmount));
165         colorComponents.components[2] = 1 - (oneMinusAmount + colorComponents.components[2] * (m_amount - oneMinusAmount));
166         return true;
167     }
168     case CONTRAST: {
169         float intercept = -(0.5f * m_amount) + 0.5f;
170         colorComponents.components[0] = clampTo<float>(intercept + m_amount * colorComponents.components[0], 0, 1);
171         colorComponents.components[1] = clampTo<float>(intercept + m_amount * colorComponents.components[1], 0, 1);
172         colorComponents.components[2] = clampTo<float>(intercept + m_amount * colorComponents.components[2], 0, 1);
173         return true;
174     }
175     case BRIGHTNESS:
176         colorComponents.components[0] = std::max<float>(m_amount * colorComponents.components[0], 0);
177         colorComponents.components[1] = std::max<float>(m_amount * colorComponents.components[1], 0);
178         colorComponents.components[2] = std::max<float>(m_amount * colorComponents.components[2], 0);
179         return true;
180     default:
181         ASSERT_NOT_REACHED();
182         return false;
183     }
184     return false;
185 }
186
187 inline bool BasicComponentTransferFilterOperation::operator==(const FilterOperation& operation) const
188 {
189     if (!isSameType(operation))
190         return false;
191     const BasicComponentTransferFilterOperation& other = downcast<BasicComponentTransferFilterOperation>(operation);
192     return m_amount == other.m_amount;
193 }
194
195 double BasicComponentTransferFilterOperation::passthroughAmount() const
196 {
197     switch (m_type) {
198     case OPACITY:
199         return 1;
200     case INVERT:
201         return 0;
202     case CONTRAST:
203         return 1;
204     case BRIGHTNESS:
205         return 1;
206     default:
207         ASSERT_NOT_REACHED();
208         return 0;
209     }
210 }
211     
212 bool InvertLightnessFilterOperation::operator==(const FilterOperation& operation) const
213 {
214     if (!isSameType(operation))
215         return false;
216
217     return true;
218 }
219     
220 RefPtr<FilterOperation> InvertLightnessFilterOperation::blend(const FilterOperation* from, double, bool)
221 {
222     if (from && !from->isSameType(*this))
223         return this;
224
225     // This filter is not currently blendable.
226     return InvertLightnessFilterOperation::create();
227 }
228
229 bool InvertLightnessFilterOperation::transformColor(ColorComponents<float>& sRGBColorComponents) const
230 {
231     auto hslComponents = sRGBToHSL(sRGBColorComponents);
232     
233     // Rotate the hue 180deg.
234     hslComponents.components[0] = fmod(hslComponents.components[0] + 0.5f, 1.0f);
235     
236     // Convert back to RGB.
237     sRGBColorComponents = hslToSRGB(hslComponents);
238     
239     // Apply the matrix. See rdar://problem/41146650 for how this matrix was derived.
240     constexpr ColorMatrix<5, 3> toDarkModeMatrix {
241        -0.770f,  0.059f, -0.089f, 0.0f, 1.0f,
242         0.030f, -0.741f, -0.089f, 0.0f, 1.0f,
243         0.030f,  0.059f, -0.890f, 0.0f, 1.0f
244     };
245     toDarkModeMatrix.transformColorComponents(sRGBColorComponents);
246     return true;
247 }
248
249 bool InvertLightnessFilterOperation::inverseTransformColor(ColorComponents<float>& sRGBColorComponents) const
250 {
251     auto rgbComponents = sRGBColorComponents;
252
253     // Apply the matrix.
254     constexpr ColorMatrix<5, 3> toLightModeMatrix {
255         -1.300f, -0.097f,  0.147f, 0.0f, 1.25f,
256         -0.049f, -1.347f,  0.146f, 0.0f, 1.25f,
257         -0.049f, -0.097f, -1.104f, 0.0f, 1.25f
258     };
259     toLightModeMatrix.transformColorComponents(rgbComponents);
260
261     // Convert to HSL.
262     auto hslComponents = sRGBToHSL(rgbComponents);
263     // Hue rotate by 180deg.
264     hslComponents.components[0] = fmod(hslComponents.components[0] + 0.5f, 1.0f);
265     // And return RGB.
266     sRGBColorComponents = hslToSRGB(hslComponents);
267     return true;
268 }
269
270 bool BlurFilterOperation::operator==(const FilterOperation& operation) const
271 {
272     if (!isSameType(operation))
273         return false;
274     
275     return m_stdDeviation == downcast<BlurFilterOperation>(operation).stdDeviation();
276 }
277     
278 RefPtr<FilterOperation> BlurFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
279 {
280     if (from && !from->isSameType(*this))
281         return this;
282
283     LengthType lengthType = m_stdDeviation.type();
284
285     if (blendToPassthrough)
286         return BlurFilterOperation::create(WebCore::blend(m_stdDeviation, Length(lengthType), progress));
287
288     const BlurFilterOperation* fromOperation = downcast<BlurFilterOperation>(from);
289     Length fromLength = fromOperation ? fromOperation->m_stdDeviation : Length(lengthType);
290     return BlurFilterOperation::create(WebCore::blend(fromLength, m_stdDeviation, progress));
291 }
292     
293 bool DropShadowFilterOperation::operator==(const FilterOperation& operation) const
294 {
295     if (!isSameType(operation))
296         return false;
297     const DropShadowFilterOperation& other = downcast<DropShadowFilterOperation>(operation);
298     return m_location == other.m_location && m_stdDeviation == other.m_stdDeviation && m_color == other.m_color;
299 }
300     
301 RefPtr<FilterOperation> DropShadowFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
302 {
303     if (from && !from->isSameType(*this))
304         return this;
305
306     if (blendToPassthrough)
307         return DropShadowFilterOperation::create(
308             WebCore::blend(m_location, IntPoint(), progress),
309             WebCore::blend(m_stdDeviation, 0, progress),
310             WebCore::blend(m_color, Color(Color::transparent), progress));
311
312     const DropShadowFilterOperation* fromOperation = downcast<DropShadowFilterOperation>(from);
313     IntPoint fromLocation = fromOperation ? fromOperation->location() : IntPoint();
314     int fromStdDeviation = fromOperation ? fromOperation->stdDeviation() : 0;
315     Color fromColor = fromOperation ? fromOperation->color() : Color(Color::transparent);
316     
317     return DropShadowFilterOperation::create(
318         WebCore::blend(fromLocation, m_location, progress),
319         WebCore::blend(fromStdDeviation, m_stdDeviation, progress),
320         WebCore::blend(fromColor, m_color, progress));
321 }
322
323 TextStream& operator<<(TextStream& ts, const FilterOperation& filter)
324 {
325     switch (filter.type()) {
326     case FilterOperation::REFERENCE:
327         ts << "reference";
328         break;
329     case FilterOperation::GRAYSCALE: {
330         const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
331         ts << "grayscale(" << colorMatrixFilter.amount() << ")";
332         break;
333     }
334     case FilterOperation::SEPIA: {
335         const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
336         ts << "sepia(" << colorMatrixFilter.amount() << ")";
337         break;
338     }
339     case FilterOperation::SATURATE: {
340         const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
341         ts << "saturate(" << colorMatrixFilter.amount() << ")";
342         break;
343     }
344     case FilterOperation::HUE_ROTATE: {
345         const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
346         ts << "hue-rotate(" << colorMatrixFilter.amount() << ")";
347         break;
348     }
349     case FilterOperation::INVERT: {
350         const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
351         ts << "invert(" << componentTransferFilter.amount() << ")";
352         break;
353     }
354     case FilterOperation::APPLE_INVERT_LIGHTNESS: {
355         ts << "apple-invert-lightness()";
356         break;
357     }
358     case FilterOperation::OPACITY: {
359         const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
360         ts << "opacity(" << componentTransferFilter.amount() << ")";
361         break;
362     }
363     case FilterOperation::BRIGHTNESS: {
364         const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
365         ts << "brightness(" << componentTransferFilter.amount() << ")";
366         break;
367     }
368     case FilterOperation::CONTRAST: {
369         const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
370         ts << "contrast(" << componentTransferFilter.amount() << ")";
371         break;
372     }
373     case FilterOperation::BLUR: {
374         const auto& blurFilter = downcast<BlurFilterOperation>(filter);
375         ts << "blur(" << blurFilter.stdDeviation().value() << ")"; // FIXME: should call floatValueForLength() but that's outisde of platform/.
376         break;
377     }
378     case FilterOperation::DROP_SHADOW: {
379         const auto& dropShadowFilter = downcast<DropShadowFilterOperation>(filter);
380         ts << "drop-shadow(" << dropShadowFilter.x() << " " << dropShadowFilter.y() << " " << dropShadowFilter.location() << " ";
381         ts << dropShadowFilter.color() << ")";
382         break;
383     }
384     case FilterOperation::PASSTHROUGH:
385         ts << "passthrough";
386         break;
387     case FilterOperation::DEFAULT: {
388         const auto& defaultFilter = downcast<DefaultFilterOperation>(filter);
389         ts << "default type=" << (int)defaultFilter.representedType();
390         break;
391     }
392     case FilterOperation::NONE:
393         ts << "none";
394         break;
395     }
396     return ts;
397 }
398
399 } // namespace WebCore