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