/* * Copyright (C) 2011 Apple Inc. All rights reserved. * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "CSSCrossfadeValue.h" #include "AnimationUtilities.h" #include "CSSImageValue.h" #include "CachedImage.h" #include "CachedResourceLoader.h" #include "CrossfadeGeneratedImage.h" #include "ImageBuffer.h" #include "RenderElement.h" #include "StyleCachedImage.h" #include "StyleGeneratedImage.h" #include namespace WebCore { static inline double blendFunc(double from, double to, double progress) { return blend(from, to, progress); } static bool subimageKnownToBeOpaque(const CSSValue& value, const RenderElement& renderer) { if (is(value)) return downcast(value).knownToBeOpaque(&renderer); if (is(value)) return downcast(value).knownToBeOpaque(renderer); ASSERT_NOT_REACHED(); return false; } inline CSSCrossfadeValue::SubimageObserver::SubimageObserver(CSSCrossfadeValue& owner) : m_owner(owner) { } void CSSCrossfadeValue::SubimageObserver::imageChanged(CachedImage*, const IntRect*) { m_owner.crossfadeChanged(); } inline CSSCrossfadeValue::CSSCrossfadeValue(Ref&& fromValue, Ref&& toValue, Ref&& percentageValue, bool prefixed) : CSSImageGeneratorValue(CrossfadeClass) , m_fromValue(WTFMove(fromValue)) , m_toValue(WTFMove(toValue)) , m_percentageValue(WTFMove(percentageValue)) , m_subimageObserver(*this) , m_isPrefixed(prefixed) { } Ref CSSCrossfadeValue::create(Ref&& fromValue, Ref&& toValue, Ref&& percentageValue, bool prefixed) { return adoptRef(*new CSSCrossfadeValue(WTFMove(fromValue), WTFMove(toValue), WTFMove(percentageValue), prefixed)); } CSSCrossfadeValue::~CSSCrossfadeValue() { if (m_cachedFromImage) m_cachedFromImage->removeClient(m_subimageObserver); if (m_cachedToImage) m_cachedToImage->removeClient(m_subimageObserver); } String CSSCrossfadeValue::customCSSText() const { StringBuilder result; if (m_isPrefixed) result.appendLiteral("-webkit-cross-fade("); else result.appendLiteral("cross-fade("); result.append(m_fromValue->cssText()); result.appendLiteral(", "); result.append(m_toValue->cssText()); result.appendLiteral(", "); result.append(m_percentageValue->cssText()); result.append(')'); return result.toString(); } FloatSize CSSCrossfadeValue::fixedSize(const RenderElement& renderer) { float percentage = m_percentageValue->floatValue(); float inversePercentage = 1 - percentage; // FIXME: Skip Content Security Policy check when cross fade is applied to an element in a user agent shadow tree. // See . auto options = CachedResourceLoader::defaultCachedResourceOptions(); auto& cachedResourceLoader = renderer.document().cachedResourceLoader(); auto* cachedFromImage = cachedImageForCSSValue(m_fromValue, cachedResourceLoader, options); auto* cachedToImage = cachedImageForCSSValue(m_toValue, cachedResourceLoader, options); if (!cachedFromImage || !cachedToImage) return FloatSize(); FloatSize fromImageSize = cachedFromImage->imageForRenderer(&renderer)->size(); FloatSize toImageSize = cachedToImage->imageForRenderer(&renderer)->size(); // Rounding issues can cause transitions between images of equal size to return // a different fixed size; avoid performing the interpolation if the images are the same size. if (fromImageSize == toImageSize) return fromImageSize; return fromImageSize * inversePercentage + toImageSize * percentage; } bool CSSCrossfadeValue::isPending() const { return CSSImageGeneratorValue::subimageIsPending(m_fromValue) || CSSImageGeneratorValue::subimageIsPending(m_toValue); } bool CSSCrossfadeValue::knownToBeOpaque(const RenderElement& renderer) const { return subimageKnownToBeOpaque(m_fromValue, renderer) && subimageKnownToBeOpaque(m_toValue, renderer); } void CSSCrossfadeValue::loadSubimages(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options) { auto oldCachedFromImage = m_cachedFromImage; auto oldCachedToImage = m_cachedToImage; m_cachedFromImage = CSSImageGeneratorValue::cachedImageForCSSValue(m_fromValue, cachedResourceLoader, options); m_cachedToImage = CSSImageGeneratorValue::cachedImageForCSSValue(m_toValue, cachedResourceLoader, options); if (m_cachedFromImage != oldCachedFromImage) { if (oldCachedFromImage) oldCachedFromImage->removeClient(m_subimageObserver); if (m_cachedFromImage) m_cachedFromImage->addClient(m_subimageObserver); } if (m_cachedToImage != oldCachedToImage) { if (oldCachedToImage) oldCachedToImage->removeClient(m_subimageObserver); if (m_cachedToImage) m_cachedToImage->addClient(m_subimageObserver); } // FIXME: Unclear why this boolean adds any value; for now keeping it around to avoid changing semantics. m_subimagesAreReady = true; } Image* CSSCrossfadeValue::image(RenderElement& renderer, const FloatSize& size) { if (size.isEmpty()) return nullptr; // FIXME: Skip Content Security Policy check when cross fade is applied to an element in a user agent shadow tree. // See . auto options = CachedResourceLoader::defaultCachedResourceOptions(); auto& cachedResourceLoader = renderer.document().cachedResourceLoader(); auto* cachedFromImage = cachedImageForCSSValue(m_fromValue, cachedResourceLoader, options); auto* cachedToImage = cachedImageForCSSValue(m_toValue, cachedResourceLoader, options); if (!cachedFromImage || !cachedToImage) return Image::nullImage(); auto* fromImage = cachedFromImage->imageForRenderer(&renderer); auto* toImage = cachedToImage->imageForRenderer(&renderer); if (!fromImage || !toImage) return Image::nullImage(); m_generatedImage = CrossfadeGeneratedImage::create(*fromImage, *toImage, m_percentageValue->floatValue(), fixedSize(renderer), size); return m_generatedImage.get(); } inline void CSSCrossfadeValue::crossfadeChanged() { if (!m_subimagesAreReady) return; for (auto& client : clients()) client.key->imageChanged(this); } bool CSSCrossfadeValue::traverseSubresources(const std::function& handler) const { if (m_cachedFromImage && handler(*m_cachedFromImage)) return true; if (m_cachedToImage && handler(*m_cachedToImage)) return true; return false; } RefPtr CSSCrossfadeValue::blend(const CSSCrossfadeValue& from, double progress) const { ASSERT(equalInputImages(from)); if (!m_cachedToImage || !m_cachedFromImage) return nullptr; auto fromImageValue = CSSImageValue::create(*m_cachedFromImage); auto toImageValue = CSSImageValue::create(*m_cachedToImage); double fromPercentage = from.m_percentageValue->doubleValue(); if (from.m_percentageValue->isPercentage()) fromPercentage /= 100.0; double toPercentage = m_percentageValue->doubleValue(); if (m_percentageValue->isPercentage()) toPercentage /= 100.0; auto percentageValue = CSSPrimitiveValue::create(blendFunc(fromPercentage, toPercentage, progress), CSSPrimitiveValue::CSS_NUMBER); return CSSCrossfadeValue::create(WTFMove(fromImageValue), WTFMove(toImageValue), WTFMove(percentageValue), from.isPrefixed() && isPrefixed()); } bool CSSCrossfadeValue::equals(const CSSCrossfadeValue& other) const { return equalInputImages(other) && compareCSSValue(m_percentageValue, other.m_percentageValue); } bool CSSCrossfadeValue::equalInputImages(const CSSCrossfadeValue& other) const { return compareCSSValue(m_fromValue, other.m_fromValue) && compareCSSValue(m_toValue, other.m_toValue); } } // namespace WebCore