246 lines
9.4 KiB
C++
246 lines
9.4 KiB
C++
/*
|
|
* Copyright (C) 2011-2021 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 "RenderElement.h"
|
|
#include "StyleBuilderState.h"
|
|
#include "StyleCachedImage.h"
|
|
|
|
namespace WebCore {
|
|
|
|
static inline double blendFunc(double from, double to, const BlendingContext& context)
|
|
{
|
|
return blend(from, to, context);
|
|
}
|
|
|
|
static bool subimageKnownToBeOpaque(const CSSValue& value, const RenderElement& renderer)
|
|
{
|
|
if (is<CSSImageValue>(value))
|
|
return downcast<CSSImageValue>(value).knownToBeOpaque(renderer);
|
|
|
|
if (is<CSSImageGeneratorValue>(value))
|
|
return downcast<CSSImageGeneratorValue>(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<CSSValue>&& fromValue, Ref<CSSValue>&& toValue, Ref<CSSPrimitiveValue>&& 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> CSSCrossfadeValue::create(Ref<CSSValue>&& fromValue, Ref<CSSValue>&& toValue, Ref<CSSPrimitiveValue>&& 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
|
|
{
|
|
return makeString(m_isPrefixed ? "-webkit-" : "", "cross-fade(", m_fromValue->cssText(), ", ", m_toValue->cssText(), ", ", m_percentageValue->cssText(), ')');
|
|
}
|
|
|
|
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 <https://bugs.webkit.org/show_bug.cgi?id=146663>.
|
|
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 <https://bugs.webkit.org/show_bug.cgi?id=146663>.
|
|
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 WTF::Function<bool (const CachedResource&)>& handler) const
|
|
{
|
|
if (m_cachedFromImage && handler(*m_cachedFromImage))
|
|
return true;
|
|
if (m_cachedToImage && handler(*m_cachedToImage))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
RefPtr<CSSCrossfadeValue> CSSCrossfadeValue::blend(const CSSCrossfadeValue& from, const BlendingContext& context) 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, context), CSSUnitType::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);
|
|
}
|
|
|
|
Ref<CSSCrossfadeValue> CSSCrossfadeValue::valueWithStylesResolved(Style::BuilderState& state)
|
|
{
|
|
auto fromValue = state.resolveImageStyles(m_fromValue.get());
|
|
auto toValue = state.resolveImageStyles(m_toValue.get());
|
|
if (fromValue.ptr() == m_fromValue.ptr() && toValue.ptr() == m_toValue.ptr())
|
|
return *this;
|
|
return create(WTFMove(fromValue), WTFMove(toValue), Ref { m_percentageValue }, m_isPrefixed);
|
|
}
|
|
|
|
} // namespace WebCore
|