2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, 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 COMPUTER, 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.
27 #include "core/css/CSSCrossfadeValue.h"
29 #include "core/css/CSSImageValue.h"
30 #include "core/layout/LayoutObject.h"
31 #include "core/style/StyleFetchedImage.h"
32 #include "platform/graphics/CrossfadeGeneratedImage.h"
33 #include "wtf/text/StringBuilder.h"
37 static bool subimageIsPending(CSSValue
* value
)
39 if (value
->isImageValue())
40 return toCSSImageValue(value
)->cachedOrPendingImage()->isPendingImage();
42 if (value
->isImageGeneratorValue())
43 return toCSSImageGeneratorValue(value
)->isPending();
50 static bool subimageKnownToBeOpaque(CSSValue
* value
, const LayoutObject
* layoutObject
)
52 if (value
->isImageValue())
53 return toCSSImageValue(value
)->knownToBeOpaque(layoutObject
);
55 if (value
->isImageGeneratorValue())
56 return toCSSImageGeneratorValue(value
)->knownToBeOpaque(layoutObject
);
63 static ImageResource
* cachedImageForCSSValue(CSSValue
* value
, Document
* document
)
68 if (value
->isImageValue()) {
69 StyleFetchedImage
* styleImageResource
= toCSSImageValue(value
)->cachedImage(document
);
70 if (!styleImageResource
)
73 return styleImageResource
->cachedImage();
76 if (value
->isImageGeneratorValue()) {
77 toCSSImageGeneratorValue(value
)->loadSubimages(document
);
78 // FIXME: Handle CSSImageGeneratorValue (and thus cross-fades with gradients and canvas).
87 static Image
* renderableImageForCSSValue(CSSValue
* value
, const LayoutObject
* layoutObject
)
89 ImageResource
* cachedImage
= cachedImageForCSSValue(value
, &layoutObject
->document());
91 // If the image can be rendered at 1 zoom it will have non-empty dimension
92 // and should be able to render at other scales as well.
93 if (!cachedImage
|| !cachedImage
->canRender(*layoutObject
, 1))
96 return cachedImage
->imageForLayoutObject(layoutObject
);
99 CSSCrossfadeValue::~CSSCrossfadeValue()
101 if (m_cachedFromImage
)
102 m_cachedFromImage
->removeClient(&m_crossfadeSubimageObserver
);
104 m_cachedToImage
->removeClient(&m_crossfadeSubimageObserver
);
107 String
CSSCrossfadeValue::customCSSText() const
109 StringBuilder result
;
110 result
.appendLiteral("-webkit-cross-fade(");
111 result
.append(m_fromValue
->cssText());
112 result
.appendLiteral(", ");
113 result
.append(m_toValue
->cssText());
114 result
.appendLiteral(", ");
115 result
.append(m_percentageValue
->cssText());
117 return result
.toString();
120 PassRefPtrWillBeRawPtr
<CSSCrossfadeValue
> CSSCrossfadeValue::valueWithURLsMadeAbsolute()
122 RefPtrWillBeRawPtr
<CSSValue
> fromValue
= m_fromValue
;
123 if (m_fromValue
->isImageValue())
124 fromValue
= toCSSImageValue(*m_fromValue
).valueWithURLMadeAbsolute();
125 RefPtrWillBeRawPtr
<CSSValue
> toValue
= m_toValue
;
126 if (m_toValue
->isImageValue())
127 toValue
= toCSSImageValue(*m_toValue
).valueWithURLMadeAbsolute();
128 return CSSCrossfadeValue::create(fromValue
.release(), toValue
.release(), m_percentageValue
);
131 IntSize
CSSCrossfadeValue::fixedSize(const LayoutObject
* layoutObject
)
133 Image
* fromImage
= renderableImageForCSSValue(m_fromValue
.get(), layoutObject
);
134 Image
* toImage
= renderableImageForCSSValue(m_toValue
.get(), layoutObject
);
136 if (!fromImage
|| !toImage
)
139 IntSize fromImageSize
= fromImage
->size();
140 IntSize toImageSize
= toImage
->size();
142 // Rounding issues can cause transitions between images of equal size to return
143 // a different fixed size; avoid performing the interpolation if the images are the same size.
144 if (fromImageSize
== toImageSize
)
145 return fromImageSize
;
147 float percentage
= m_percentageValue
->getFloatValue();
148 float inversePercentage
= 1 - percentage
;
150 return IntSize(fromImageSize
.width() * inversePercentage
+ toImageSize
.width() * percentage
,
151 fromImageSize
.height() * inversePercentage
+ toImageSize
.height() * percentage
);
154 bool CSSCrossfadeValue::isPending() const
156 return subimageIsPending(m_fromValue
.get()) || subimageIsPending(m_toValue
.get());
159 bool CSSCrossfadeValue::knownToBeOpaque(const LayoutObject
* layoutObject
) const
161 return subimageKnownToBeOpaque(m_fromValue
.get(), layoutObject
) && subimageKnownToBeOpaque(m_toValue
.get(), layoutObject
);
164 void CSSCrossfadeValue::loadSubimages(Document
* document
)
166 ResourcePtr
<ImageResource
> oldCachedFromImage
= m_cachedFromImage
;
167 ResourcePtr
<ImageResource
> oldCachedToImage
= m_cachedToImage
;
169 m_cachedFromImage
= cachedImageForCSSValue(m_fromValue
.get(), document
);
170 m_cachedToImage
= cachedImageForCSSValue(m_toValue
.get(), document
);
172 if (m_cachedFromImage
!= oldCachedFromImage
) {
173 if (oldCachedFromImage
)
174 oldCachedFromImage
->removeClient(&m_crossfadeSubimageObserver
);
175 if (m_cachedFromImage
)
176 m_cachedFromImage
->addClient(&m_crossfadeSubimageObserver
);
179 if (m_cachedToImage
!= oldCachedToImage
) {
180 if (oldCachedToImage
)
181 oldCachedToImage
->removeClient(&m_crossfadeSubimageObserver
);
183 m_cachedToImage
->addClient(&m_crossfadeSubimageObserver
);
186 m_crossfadeSubimageObserver
.setReady(true);
189 PassRefPtr
<Image
> CSSCrossfadeValue::image(LayoutObject
* layoutObject
, const IntSize
& size
)
194 Image
* fromImage
= renderableImageForCSSValue(m_fromValue
.get(), layoutObject
);
195 Image
* toImage
= renderableImageForCSSValue(m_toValue
.get(), layoutObject
);
197 if (!fromImage
|| !toImage
)
198 return Image::nullImage();
200 m_generatedImage
= CrossfadeGeneratedImage::create(fromImage
, toImage
, m_percentageValue
->getFloatValue(), fixedSize(layoutObject
), size
);
202 return m_generatedImage
.release();
205 void CSSCrossfadeValue::crossfadeChanged(const IntRect
&)
207 for (const auto& curr
: clients()) {
208 LayoutObject
* client
= const_cast<LayoutObject
*>(curr
.key
);
209 client
->imageChanged(static_cast<WrappedImagePtr
>(this));
213 void CSSCrossfadeValue::CrossfadeSubimageObserverProxy::imageChanged(ImageResource
*, const IntRect
* rect
)
216 m_ownerValue
->crossfadeChanged(*rect
);
219 bool CSSCrossfadeValue::hasFailedOrCanceledSubresources() const
221 if (m_cachedFromImage
&& m_cachedFromImage
->loadFailedOrCanceled())
223 if (m_cachedToImage
&& m_cachedToImage
->loadFailedOrCanceled())
228 bool CSSCrossfadeValue::equals(const CSSCrossfadeValue
& other
) const
230 return compareCSSValuePtr(m_fromValue
, other
.m_fromValue
)
231 && compareCSSValuePtr(m_toValue
, other
.m_toValue
)
232 && compareCSSValuePtr(m_percentageValue
, other
.m_percentageValue
);
235 DEFINE_TRACE_AFTER_DISPATCH(CSSCrossfadeValue
)
237 visitor
->trace(m_fromValue
);
238 visitor
->trace(m_toValue
);
239 visitor
->trace(m_percentageValue
);
240 visitor
->trace(m_crossfadeSubimageObserver
);
241 CSSImageGeneratorValue::traceAfterDispatch(visitor
);