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