741865089fefeb68ecb69da60d9bd05df653cb3c
[WebKit-https.git] / Source / WebCore / css / CSSCrossfadeValue.cpp
1 /*
2  * Copyright (C) 2011 Apple Inc.  All rights reserved.
3  * Copyright (C) 2013 Adobe Systems Incorporated. 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  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "CSSCrossfadeValue.h"
29
30 #include "AnimationUtilities.h"
31 #include "CSSImageValue.h"
32 #include "CachedResourceLoader.h"
33 #include "CrossfadeGeneratedImage.h"
34 #include "ImageBuffer.h"
35 #include "RenderElement.h"
36 #include "StyleCachedImage.h"
37 #include "StyleGeneratedImage.h"
38 #include <wtf/text/StringBuilder.h>
39
40 namespace WebCore {
41
42 static inline double blendFunc(double from, double to, double progress)
43 {
44     return blend(from, to, progress);
45 }
46
47 static bool subimageKnownToBeOpaque(CSSValue& value, const RenderElement* renderer)
48 {
49     if (value.isImageValue())
50         return toCSSImageValue(value).knownToBeOpaque(renderer);
51
52     if (value.isImageGeneratorValue())
53         return toCSSImageGeneratorValue(value).knownToBeOpaque(renderer);
54
55     ASSERT_NOT_REACHED();
56
57     return false;
58 }
59
60 CSSCrossfadeValue::~CSSCrossfadeValue()
61 {
62     if (m_cachedFromImage)
63         m_cachedFromImage->removeClient(&m_crossfadeSubimageObserver);
64     if (m_cachedToImage)
65         m_cachedToImage->removeClient(&m_crossfadeSubimageObserver);
66 }
67
68 String CSSCrossfadeValue::customCSSText() const
69 {
70     StringBuilder result;
71     result.appendLiteral("-webkit-cross-fade(");
72     result.append(m_fromValue->cssText());
73     result.appendLiteral(", ");
74     result.append(m_toValue->cssText());
75     result.appendLiteral(", ");
76     result.append(m_percentageValue->cssText());
77     result.append(')');
78     return result.toString();
79 }
80
81 IntSize CSSCrossfadeValue::fixedSize(const RenderElement* renderer)
82 {
83     float percentage = m_percentageValue->getFloatValue();
84     float inversePercentage = 1 - percentage;
85
86     CachedResourceLoader* cachedResourceLoader = renderer->document().cachedResourceLoader();
87     CachedImage* cachedFromImage = cachedImageForCSSValue(m_fromValue.get(), cachedResourceLoader);
88     CachedImage* cachedToImage = cachedImageForCSSValue(m_toValue.get(), cachedResourceLoader);
89
90     if (!cachedFromImage || !cachedToImage)
91         return IntSize();
92
93     FloatSize fromImageSize = cachedFromImage->imageForRenderer(renderer)->size();
94     FloatSize toImageSize = cachedToImage->imageForRenderer(renderer)->size();
95
96     // Rounding issues can cause transitions between images of equal size to return
97     // a different fixed size; avoid performing the interpolation if the images are the same size.
98     if (fromImageSize == toImageSize)
99         return IntSize(fromImageSize);
100
101     return IntSize(fromImageSize.width() * inversePercentage + toImageSize.width() * percentage,
102         fromImageSize.height() * inversePercentage + toImageSize.height() * percentage);
103 }
104
105 bool CSSCrossfadeValue::isPending() const
106 {
107     return CSSImageGeneratorValue::subimageIsPending(m_fromValue.get())
108         || CSSImageGeneratorValue::subimageIsPending(m_toValue.get());
109 }
110
111 bool CSSCrossfadeValue::knownToBeOpaque(const RenderElement* renderer) const
112 {
113     return subimageKnownToBeOpaque(*m_fromValue, renderer) && subimageKnownToBeOpaque(*m_toValue, renderer);
114 }
115
116 void CSSCrossfadeValue::loadSubimages(CachedResourceLoader* cachedResourceLoader)
117 {
118     CachedResourceHandle<CachedImage> oldCachedFromImage = m_cachedFromImage;
119     CachedResourceHandle<CachedImage> oldCachedToImage = m_cachedToImage;
120
121     m_cachedFromImage = CSSImageGeneratorValue::cachedImageForCSSValue(m_fromValue.get(), cachedResourceLoader);
122     m_cachedToImage = CSSImageGeneratorValue::cachedImageForCSSValue(m_toValue.get(), cachedResourceLoader);
123
124     if (m_cachedFromImage != oldCachedFromImage) {
125         if (oldCachedFromImage)
126             oldCachedFromImage->removeClient(&m_crossfadeSubimageObserver);
127         if (m_cachedFromImage)
128             m_cachedFromImage->addClient(&m_crossfadeSubimageObserver);
129     }
130
131     if (m_cachedToImage != oldCachedToImage) {
132         if (oldCachedToImage)
133             oldCachedToImage->removeClient(&m_crossfadeSubimageObserver);
134         if (m_cachedToImage)
135             m_cachedToImage->addClient(&m_crossfadeSubimageObserver);
136     }
137
138     m_crossfadeSubimageObserver.setReady(true);
139 }
140
141 PassRefPtr<Image> CSSCrossfadeValue::image(RenderElement* renderer, const IntSize& size)
142 {
143     if (size.isEmpty())
144         return 0;
145
146     CachedResourceLoader* cachedResourceLoader = renderer->document().cachedResourceLoader();
147     CachedImage* cachedFromImage = cachedImageForCSSValue(m_fromValue.get(), cachedResourceLoader);
148     CachedImage* cachedToImage = cachedImageForCSSValue(m_toValue.get(), cachedResourceLoader);
149
150     if (!cachedFromImage || !cachedToImage)
151         return Image::nullImage();
152
153     Image* fromImage = cachedFromImage->imageForRenderer(renderer);
154     Image* toImage = cachedToImage->imageForRenderer(renderer);
155
156     if (!fromImage || !toImage)
157         return Image::nullImage();
158
159     m_generatedImage = CrossfadeGeneratedImage::create(fromImage, toImage, m_percentageValue->getFloatValue(), fixedSize(renderer), size);
160
161     return m_generatedImage.release();
162 }
163
164 void CSSCrossfadeValue::crossfadeChanged(const IntRect&)
165 {
166     for (auto it = clients().begin(), end = clients().end(); it != end; ++it)
167         it->key->imageChanged(static_cast<WrappedImagePtr>(this));
168 }
169
170 void CSSCrossfadeValue::CrossfadeSubimageObserverProxy::imageChanged(CachedImage*, const IntRect* rect)
171 {
172     if (m_ready)
173         m_ownerValue->crossfadeChanged(*rect);
174 }
175
176 bool CSSCrossfadeValue::hasFailedOrCanceledSubresources() const
177 {
178     if (m_cachedFromImage && m_cachedFromImage->loadFailedOrCanceled())
179         return true;
180     if (m_cachedToImage && m_cachedToImage->loadFailedOrCanceled())
181         return true;
182     return false;
183 }
184
185 PassRefPtr<CSSCrossfadeValue> CSSCrossfadeValue::blend(const CSSCrossfadeValue& from, double progress) const
186 {
187     ASSERT(equalInputImages(from));
188     RefPtr<StyleCachedImage> toStyledImage = StyleCachedImage::create(m_cachedToImage.get());
189     RefPtr<StyleCachedImage> fromStyledImage = StyleCachedImage::create(m_cachedFromImage.get());
190
191     auto fromImageValue = CSSImageValue::create(m_cachedFromImage->url(), fromStyledImage.get());
192     auto toImageValue = CSSImageValue::create(m_cachedToImage->url(), toStyledImage.get());
193
194     RefPtr<CSSCrossfadeValue> crossfadeValue = CSSCrossfadeValue::create(std::move(fromImageValue), std::move(toImageValue));
195
196     double fromPercentage = from.m_percentageValue->getDoubleValue();
197     if (from.m_percentageValue->isPercentage())
198         fromPercentage /= 100.0;
199     double toPercentage = m_percentageValue->getDoubleValue();
200     if (m_percentageValue->isPercentage())
201         toPercentage /= 100.0;
202     crossfadeValue->setPercentage(CSSPrimitiveValue::create(blendFunc(fromPercentage, toPercentage, progress), CSSPrimitiveValue::CSS_NUMBER));
203     return crossfadeValue.release();
204 }
205
206 bool CSSCrossfadeValue::equals(const CSSCrossfadeValue& other) const
207 {
208     return equalInputImages(other)
209         && compareCSSValuePtr(m_percentageValue, other.m_percentageValue);
210 }
211
212
213 bool CSSCrossfadeValue::equalInputImages(const CSSCrossfadeValue& other) const
214 {
215     return compareCSSValuePtr(m_fromValue, other.m_fromValue)
216         && compareCSSValuePtr(m_toValue, other.m_toValue);
217 }
218
219 } // namespace WebCore