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 "CachedImage.h" 33#include "CachedResourceLoader.h" 34#include "CrossfadeGeneratedImage.h" 35#include "ImageBuffer.h" 36#include "RenderElement.h" 37#include "StyleCachedImage.h" 38#include "StyleGeneratedImage.h" 39#include <wtf/text/StringBuilder.h> 40 41namespace WebCore { 42 43static inline double blendFunc(double from, double to, double progress) 44{ 45 return blend(from, to, progress); 46} 47 48static bool subimageKnownToBeOpaque(CSSValue& value, const RenderElement* renderer) 49{ 50 if (value.isImageValue()) 51 return toCSSImageValue(value).knownToBeOpaque(renderer); 52 53 if (value.isImageGeneratorValue()) 54 return toCSSImageGeneratorValue(value).knownToBeOpaque(renderer); 55 56 ASSERT_NOT_REACHED(); 57 58 return false; 59} 60 61CSSCrossfadeValue::~CSSCrossfadeValue() 62{ 63 if (m_cachedFromImage) 64 m_cachedFromImage->removeClient(&m_crossfadeSubimageObserver); 65 if (m_cachedToImage) 66 m_cachedToImage->removeClient(&m_crossfadeSubimageObserver); 67} 68 69String CSSCrossfadeValue::customCSSText() const 70{ 71 StringBuilder result; 72 result.appendLiteral("-webkit-cross-fade("); 73 result.append(m_fromValue->cssText()); 74 result.appendLiteral(", "); 75 result.append(m_toValue->cssText()); 76 result.appendLiteral(", "); 77 result.append(m_percentageValue->cssText()); 78 result.append(')'); 79 return result.toString(); 80} 81 82FloatSize CSSCrossfadeValue::fixedSize(const RenderElement* renderer) 83{ 84 float percentage = m_percentageValue->getFloatValue(); 85 float inversePercentage = 1 - percentage; 86 87 CachedResourceLoader* cachedResourceLoader = renderer->document().cachedResourceLoader(); 88 CachedImage* cachedFromImage = cachedImageForCSSValue(m_fromValue.get(), cachedResourceLoader); 89 CachedImage* cachedToImage = cachedImageForCSSValue(m_toValue.get(), cachedResourceLoader); 90 91 if (!cachedFromImage || !cachedToImage) 92 return FloatSize(); 93 94 FloatSize fromImageSize = cachedFromImage->imageForRenderer(renderer)->size(); 95 FloatSize toImageSize = cachedToImage->imageForRenderer(renderer)->size(); 96 97 // Rounding issues can cause transitions between images of equal size to return 98 // a different fixed size; avoid performing the interpolation if the images are the same size. 99 if (fromImageSize == toImageSize) 100 return fromImageSize; 101 102 return FloatSize(fromImageSize.width() * inversePercentage + toImageSize.width() * percentage, 103 fromImageSize.height() * inversePercentage + toImageSize.height() * percentage); 104} 105 106bool CSSCrossfadeValue::isPending() const 107{ 108 return CSSImageGeneratorValue::subimageIsPending(m_fromValue.get()) 109 || CSSImageGeneratorValue::subimageIsPending(m_toValue.get()); 110} 111 112bool CSSCrossfadeValue::knownToBeOpaque(const RenderElement* renderer) const 113{ 114 return subimageKnownToBeOpaque(*m_fromValue, renderer) && subimageKnownToBeOpaque(*m_toValue, renderer); 115} 116 117void CSSCrossfadeValue::loadSubimages(CachedResourceLoader* cachedResourceLoader) 118{ 119 CachedResourceHandle<CachedImage> oldCachedFromImage = m_cachedFromImage; 120 CachedResourceHandle<CachedImage> oldCachedToImage = m_cachedToImage; 121 122 m_cachedFromImage = CSSImageGeneratorValue::cachedImageForCSSValue(m_fromValue.get(), cachedResourceLoader); 123 m_cachedToImage = CSSImageGeneratorValue::cachedImageForCSSValue(m_toValue.get(), cachedResourceLoader); 124 125 if (m_cachedFromImage != oldCachedFromImage) { 126 if (oldCachedFromImage) 127 oldCachedFromImage->removeClient(&m_crossfadeSubimageObserver); 128 if (m_cachedFromImage) 129 m_cachedFromImage->addClient(&m_crossfadeSubimageObserver); 130 } 131 132 if (m_cachedToImage != oldCachedToImage) { 133 if (oldCachedToImage) 134 oldCachedToImage->removeClient(&m_crossfadeSubimageObserver); 135 if (m_cachedToImage) 136 m_cachedToImage->addClient(&m_crossfadeSubimageObserver); 137 } 138 139 m_crossfadeSubimageObserver.setReady(true); 140} 141 142PassRefPtr<Image> CSSCrossfadeValue::image(RenderElement* renderer, const FloatSize& size) 143{ 144 if (size.isEmpty()) 145 return 0; 146 147 CachedResourceLoader* cachedResourceLoader = renderer->document().cachedResourceLoader(); 148 CachedImage* cachedFromImage = cachedImageForCSSValue(m_fromValue.get(), cachedResourceLoader); 149 CachedImage* cachedToImage = cachedImageForCSSValue(m_toValue.get(), cachedResourceLoader); 150 151 if (!cachedFromImage || !cachedToImage) 152 return Image::nullImage(); 153 154 Image* fromImage = cachedFromImage->imageForRenderer(renderer); 155 Image* toImage = cachedToImage->imageForRenderer(renderer); 156 157 if (!fromImage || !toImage) 158 return Image::nullImage(); 159 160 m_generatedImage = CrossfadeGeneratedImage::create(fromImage, toImage, m_percentageValue->getFloatValue(), fixedSize(renderer), size); 161 162 return m_generatedImage.release(); 163} 164 165void CSSCrossfadeValue::crossfadeChanged(const IntRect&) 166{ 167 for (auto it = clients().begin(), end = clients().end(); it != end; ++it) 168 it->key->imageChanged(static_cast<WrappedImagePtr>(this)); 169} 170 171void CSSCrossfadeValue::CrossfadeSubimageObserverProxy::imageChanged(CachedImage*, const IntRect* rect) 172{ 173 if (m_ready) 174 m_ownerValue->crossfadeChanged(*rect); 175} 176 177bool CSSCrossfadeValue::hasFailedOrCanceledSubresources() const 178{ 179 if (m_cachedFromImage && m_cachedFromImage->loadFailedOrCanceled()) 180 return true; 181 if (m_cachedToImage && m_cachedToImage->loadFailedOrCanceled()) 182 return true; 183 return false; 184} 185 186PassRefPtr<CSSCrossfadeValue> CSSCrossfadeValue::blend(const CSSCrossfadeValue& from, double progress) const 187{ 188 ASSERT(equalInputImages(from)); 189 RefPtr<StyleCachedImage> toStyledImage = StyleCachedImage::create(m_cachedToImage.get()); 190 RefPtr<StyleCachedImage> fromStyledImage = StyleCachedImage::create(m_cachedFromImage.get()); 191 192 auto fromImageValue = CSSImageValue::create(m_cachedFromImage->url(), fromStyledImage.get()); 193 auto toImageValue = CSSImageValue::create(m_cachedToImage->url(), toStyledImage.get()); 194 195 RefPtr<CSSCrossfadeValue> crossfadeValue = CSSCrossfadeValue::create(WTF::move(fromImageValue), WTF::move(toImageValue)); 196 197 double fromPercentage = from.m_percentageValue->getDoubleValue(); 198 if (from.m_percentageValue->isPercentage()) 199 fromPercentage /= 100.0; 200 double toPercentage = m_percentageValue->getDoubleValue(); 201 if (m_percentageValue->isPercentage()) 202 toPercentage /= 100.0; 203 crossfadeValue->setPercentage(CSSPrimitiveValue::create(blendFunc(fromPercentage, toPercentage, progress), CSSPrimitiveValue::CSS_NUMBER)); 204 return crossfadeValue.release(); 205} 206 207bool CSSCrossfadeValue::equals(const CSSCrossfadeValue& other) const 208{ 209 return equalInputImages(other) 210 && compareCSSValuePtr(m_percentageValue, other.m_percentageValue); 211} 212 213 214bool CSSCrossfadeValue::equalInputImages(const CSSCrossfadeValue& other) const 215{ 216 return compareCSSValuePtr(m_fromValue, other.m_fromValue) 217 && compareCSSValuePtr(m_toValue, other.m_toValue); 218} 219 220} // namespace WebCore 221