Extended Color: Move ColorMatrix to its own files
[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(FloatComponents& colorComponents) const
89 {
90     switch (m_type) {
91     case GRAYSCALE: {
92         ColorMatrix matrix = ColorMatrix::grayscaleMatrix(m_amount);
93         matrix.transformColorComponents(colorComponents);
94         return true;
95     }
96     case SEPIA: {
97         ColorMatrix matrix = ColorMatrix::sepiaMatrix(m_amount);
98         matrix.transformColorComponents(colorComponents);
99         return true;
100     }
101     case HUE_ROTATE: {
102         ColorMatrix matrix = ColorMatrix::hueRotateMatrix(m_amount);
103         matrix.transformColorComponents(colorComponents);
104         return true;
105     }
106     case SATURATE: {
107         ColorMatrix matrix = ColorMatrix::saturationMatrix(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(FloatComponents& 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(FloatComponents& sRGBColorComponents) const
230 {
231     FloatComponents 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     const float matrixValues[20] = {
241        -0.770,  0.059, -0.089, 0, 1,
242         0.030, -0.741, -0.089, 0, 1,
243         0.030,  0.059, -0.890, 0, 1,
244         0,      0,      0,     1, 0
245     };
246
247     ColorMatrix toDarkModeMatrix(matrixValues);
248     toDarkModeMatrix.transformColorComponents(sRGBColorComponents);
249     return true;
250 }
251
252 bool InvertLightnessFilterOperation::inverseTransformColor(FloatComponents& sRGBColorComponents) const
253 {
254     FloatComponents rgbComponents = sRGBColorComponents;
255     // Apply the matrix.
256     const float matrixValues[20] = {
257         -1.300, -0.097,  0.147, 0, 1.25,
258         -0.049, -1.347,  0.146, 0, 1.25,
259         -0.049, -0.097, -1.104, 0, 1.25,
260          0,      0,      0,     1, 0
261     };
262     ColorMatrix toLightModeMatrix(matrixValues);
263     toLightModeMatrix.transformColorComponents(rgbComponents);
264
265     // Convert to HSL.
266     FloatComponents hslComponents = sRGBToHSL(rgbComponents);
267     // Hue rotate by 180deg.
268     hslComponents.components[0] = fmod(hslComponents.components[0] + 0.5f, 1.0f);
269     // And return RGB.
270     sRGBColorComponents = hslToSRGB(hslComponents);
271     return true;
272 }
273
274 bool BlurFilterOperation::operator==(const FilterOperation& operation) const
275 {
276     if (!isSameType(operation))
277         return false;
278     
279     return m_stdDeviation == downcast<BlurFilterOperation>(operation).stdDeviation();
280 }
281     
282 RefPtr<FilterOperation> BlurFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
283 {
284     if (from && !from->isSameType(*this))
285         return this;
286
287     LengthType lengthType = m_stdDeviation.type();
288
289     if (blendToPassthrough)
290         return BlurFilterOperation::create(WebCore::blend(m_stdDeviation, Length(lengthType), progress));
291
292     const BlurFilterOperation* fromOperation = downcast<BlurFilterOperation>(from);
293     Length fromLength = fromOperation ? fromOperation->m_stdDeviation : Length(lengthType);
294     return BlurFilterOperation::create(WebCore::blend(fromLength, m_stdDeviation, progress));
295 }
296     
297 bool DropShadowFilterOperation::operator==(const FilterOperation& operation) const
298 {
299     if (!isSameType(operation))
300         return false;
301     const DropShadowFilterOperation& other = downcast<DropShadowFilterOperation>(operation);
302     return m_location == other.m_location && m_stdDeviation == other.m_stdDeviation && m_color == other.m_color;
303 }
304     
305 RefPtr<FilterOperation> DropShadowFilterOperation::blend(const FilterOperation* from, double progress, bool blendToPassthrough)
306 {
307     if (from && !from->isSameType(*this))
308         return this;
309
310     if (blendToPassthrough)
311         return DropShadowFilterOperation::create(
312             WebCore::blend(m_location, IntPoint(), progress),
313             WebCore::blend(m_stdDeviation, 0, progress),
314             WebCore::blend(m_color, Color(Color::transparent), progress));
315
316     const DropShadowFilterOperation* fromOperation = downcast<DropShadowFilterOperation>(from);
317     IntPoint fromLocation = fromOperation ? fromOperation->location() : IntPoint();
318     int fromStdDeviation = fromOperation ? fromOperation->stdDeviation() : 0;
319     Color fromColor = fromOperation ? fromOperation->color() : Color(Color::transparent);
320     
321     return DropShadowFilterOperation::create(
322         WebCore::blend(fromLocation, m_location, progress),
323         WebCore::blend(fromStdDeviation, m_stdDeviation, progress),
324         WebCore::blend(fromColor, m_color, progress));
325 }
326
327 TextStream& operator<<(TextStream& ts, const FilterOperation& filter)
328 {
329     switch (filter.type()) {
330     case FilterOperation::REFERENCE:
331         ts << "reference";
332         break;
333     case FilterOperation::GRAYSCALE: {
334         const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
335         ts << "grayscale(" << colorMatrixFilter.amount() << ")";
336         break;
337     }
338     case FilterOperation::SEPIA: {
339         const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
340         ts << "sepia(" << colorMatrixFilter.amount() << ")";
341         break;
342     }
343     case FilterOperation::SATURATE: {
344         const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
345         ts << "saturate(" << colorMatrixFilter.amount() << ")";
346         break;
347     }
348     case FilterOperation::HUE_ROTATE: {
349         const auto& colorMatrixFilter = downcast<BasicColorMatrixFilterOperation>(filter);
350         ts << "hue-rotate(" << colorMatrixFilter.amount() << ")";
351         break;
352     }
353     case FilterOperation::INVERT: {
354         const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
355         ts << "invert(" << componentTransferFilter.amount() << ")";
356         break;
357     }
358     case FilterOperation::APPLE_INVERT_LIGHTNESS: {
359         ts << "apple-invert-lightness()";
360         break;
361     }
362     case FilterOperation::OPACITY: {
363         const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
364         ts << "opacity(" << componentTransferFilter.amount() << ")";
365         break;
366     }
367     case FilterOperation::BRIGHTNESS: {
368         const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
369         ts << "brightness(" << componentTransferFilter.amount() << ")";
370         break;
371     }
372     case FilterOperation::CONTRAST: {
373         const auto& componentTransferFilter = downcast<BasicComponentTransferFilterOperation>(filter);
374         ts << "contrast(" << componentTransferFilter.amount() << ")";
375         break;
376     }
377     case FilterOperation::BLUR: {
378         const auto& blurFilter = downcast<BlurFilterOperation>(filter);
379         ts << "blur(" << blurFilter.stdDeviation().value() << ")"; // FIXME: should call floatValueForLength() but that's outisde of platform/.
380         break;
381     }
382     case FilterOperation::DROP_SHADOW: {
383         const auto& dropShadowFilter = downcast<DropShadowFilterOperation>(filter);
384         ts << "drop-shadow(" << dropShadowFilter.x() << " " << dropShadowFilter.y() << " " << dropShadowFilter.location() << " ";
385         ts << dropShadowFilter.color() << ")";
386         break;
387     }
388     case FilterOperation::PASSTHROUGH:
389         ts << "passthrough";
390         break;
391     case FilterOperation::DEFAULT: {
392         const auto& defaultFilter = downcast<DefaultFilterOperation>(filter);
393         ts << "default type=" << (int)defaultFilter.representedType();
394         break;
395     }
396     case FilterOperation::NONE:
397         ts << "none";
398         break;
399     }
400     return ts;
401 }
402
403 } // namespace WebCore