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