Skip to content

Commit

Permalink
[threaded-animation-resolution] add support for blending filter values
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=269236
rdar://122842684

Reviewed by Simon Fraser.

We've added support for blending `opacity` with 274102@main and `transform`
with 274487@main. The last group of properties to support is those affecting
in the `CALayer.filters` property. This requires a bit more work than the
other properties since:

- the `drop-shadow()` property maps to separate `CALayer` properties,
- we cannot just compute a single `filters` value for all the other CSS
filter functions.

To that end we use a dedicated `CAPresentationModifier` per animated filter
function. An upcoming patch will ensure that only filter lists that support
interpolation (in other words, not discrete) make it up to the UIProcess,
thus guaranteeing a shared list of primitives. The canonical list of filter
functions can thus be obtained by looking at the longest list of operations
in the provided keyframes or the base value in the case where the 0% or 100%
keyframe is not explicitly provided.

That upcoming patch will also ensure that `drop-shadow()` is the last function
used in a filter list since otherwise the order in which the `CALayer.filters`
property and the properties used to reflect the `drop-shadow()` function won't
match.

This patch was co-authored with Matt Woodrow.

* Source/WebCore/platform/graphics/ca/PlatformCAFilters.h:
* Source/WebCore/platform/graphics/ca/cocoa/PlatformCAFiltersCocoa.mm:
(WebCore::keyValueCountForFilter):
(WebCore::PlatformCAFilters::presentationModifierCount):
(WebCore::passthroughFilter):
(WebCore::PlatformCAFilters::presentationModifiers):
(WebCore::PlatformCAFilters::updatePresentationModifiers):
(WebCore::PlatformCAFilters::setFiltersOnLayer):
* Source/WebKit/UIProcess/RemoteLayerTree/RemoteAcceleratedEffectStack.h:
* Source/WebKit/UIProcess/RemoteLayerTree/RemoteAcceleratedEffectStack.mm:
(WebKit::RemoteAcceleratedEffectStack::setEffects):
(WebKit::RemoteAcceleratedEffectStack::longestFilterList const):
(WebKit::RemoteAcceleratedEffectStack::initEffectsFromMainThread):
(WebKit::RemoteAcceleratedEffectStack::applyEffectsFromScrollingThread const):
(WebKit::RemoteAcceleratedEffectStack::applyEffectsFromMainThread const):
(WebKit::RemoteAcceleratedEffectStack::computeValues const):
(WebKit::RemoteAcceleratedEffectStack::clear):

Canonical link: https://commits.webkit.org/274587@main
  • Loading branch information
graouts committed Feb 13, 2024
1 parent 35318b4 commit 7591a30
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 22 deletions.
15 changes: 15 additions & 0 deletions Source/WebCore/platform/graphics/ca/PlatformCAFilters.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,19 @@

OBJC_CLASS NSValue;

#if PLATFORM(MAC)
OBJC_CLASS CAPresentationModifier;
OBJC_CLASS CAPresentationModifierGroup;
#endif

namespace WebCore {

class PlatformCALayer;

#if PLATFORM(MAC)
using TypedFilterPresentationModifier = std::pair<FilterOperation::Type, RetainPtr<CAPresentationModifier>>;
#endif

class PlatformCAFilters {
public:
WEBCORE_EXPORT static void setFiltersOnLayer(PlatformLayer*, const FilterOperations&);
Expand All @@ -48,6 +57,12 @@ class PlatformCAFilters {

// A null operation indicates that we should make a "no-op" filter of the given type.
static RetainPtr<NSValue> colorMatrixValueForFilter(FilterOperation::Type, const FilterOperation*);

#if PLATFORM(MAC)
WEBCORE_EXPORT static void presentationModifiers(const FilterOperations& initialFilters, const FilterOperations* canonicalFilters, Vector<TypedFilterPresentationModifier>& presentationModifiers, RetainPtr<CAPresentationModifierGroup>&);
WEBCORE_EXPORT static void updatePresentationModifiers(const FilterOperations& filters, const Vector<TypedFilterPresentationModifier>& presentationModifiers);
WEBCORE_EXPORT static size_t presentationModifierCount(const FilterOperations&);
#endif
};

}
184 changes: 183 additions & 1 deletion Source/WebCore/platform/graphics/ca/cocoa/PlatformCAFiltersCocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,189 @@

namespace WebCore {

#if PLATFORM(MAC)
static unsigned keyValueCountForFilter(const FilterOperation& filterOperation)
{
switch (filterOperation.type()) {
case FilterOperation::Type::Default:
case FilterOperation::Type::Reference:
case FilterOperation::Type::None:
ASSERT_NOT_REACHED();
return 0;
case FilterOperation::Type::DropShadow:
return 3;
case FilterOperation::Type::Sepia:
case FilterOperation::Type::Saturate:
case FilterOperation::Type::HueRotate:
case FilterOperation::Type::Invert:
case FilterOperation::Type::Opacity:
case FilterOperation::Type::Brightness:
case FilterOperation::Type::Contrast:
case FilterOperation::Type::Grayscale:
case FilterOperation::Type::Blur:
return 1;
case FilterOperation::Type::AppleInvertLightness:
ASSERT_NOT_REACHED(); // AppleInvertLightness is only used in -apple-color-filter.
break;
case FilterOperation::Type::Passthrough:
return 0;
}
ASSERT_NOT_REACHED();
return 0;
}

size_t PlatformCAFilters::presentationModifierCount(const FilterOperations& filters)
{
size_t count = 0;
for (const auto& filter : filters.operations())
count += keyValueCountForFilter(*filter.get());
return count;
}

static const FilterOperation& passthroughFilter(const FilterOperation::Type typeToMatch)
{
switch (typeToMatch) {
case FilterOperation::Type::DropShadow:
static NeverDestroyed<Ref<DropShadowFilterOperation>> passthroughDropShadowFilter = DropShadowFilterOperation::create({ }, 0, { });
return passthroughDropShadowFilter.get();
case FilterOperation::Type::Grayscale:
static NeverDestroyed<Ref<BasicColorMatrixFilterOperation>> passthroughGrayscaleFilter = BasicColorMatrixFilterOperation::create(0, typeToMatch);
return passthroughGrayscaleFilter.get();
case FilterOperation::Type::Sepia:
static NeverDestroyed<Ref<BasicColorMatrixFilterOperation>> passthroughSepiaFilter = BasicColorMatrixFilterOperation::create(0, typeToMatch);
return passthroughSepiaFilter.get();
case FilterOperation::Type::Saturate:
static NeverDestroyed<Ref<BasicColorMatrixFilterOperation>> passthroughSaturateFilter = BasicColorMatrixFilterOperation::create(0, typeToMatch);
return passthroughSaturateFilter.get();
case FilterOperation::Type::HueRotate:
static NeverDestroyed<Ref<BasicColorMatrixFilterOperation>> passthroughHueRotateFilter = BasicColorMatrixFilterOperation::create(0, typeToMatch);
return passthroughHueRotateFilter.get();
case FilterOperation::Type::Invert:
static NeverDestroyed<Ref<BasicColorMatrixFilterOperation>> passthroughInvertFilter = BasicColorMatrixFilterOperation::create(0, typeToMatch);
return passthroughInvertFilter.get();
case FilterOperation::Type::Opacity:
static NeverDestroyed<Ref<BasicColorMatrixFilterOperation>> passthroughOpacityFilter = BasicColorMatrixFilterOperation::create(0, typeToMatch);
return passthroughOpacityFilter.get();
case FilterOperation::Type::Brightness:
static NeverDestroyed<Ref<BasicColorMatrixFilterOperation>> passthroughBrightnessFilter = BasicColorMatrixFilterOperation::create(0, typeToMatch);
return passthroughBrightnessFilter.get();
case FilterOperation::Type::Contrast:
static NeverDestroyed<Ref<BasicColorMatrixFilterOperation>> passthroughContrastFilter = BasicColorMatrixFilterOperation::create(0, typeToMatch);
return passthroughContrastFilter.get();
case FilterOperation::Type::Blur:
static NeverDestroyed<Ref<BlurFilterOperation>> passthroughBlurFilter = BlurFilterOperation::create({ 0, LengthType::Fixed });
return passthroughBlurFilter.get();
default:
ASSERT_NOT_REACHED();
static NeverDestroyed<Ref<BasicColorMatrixFilterOperation>> passthroughDefaultFilter = BasicColorMatrixFilterOperation::create(0, FilterOperation::Type::Grayscale);
return passthroughDefaultFilter.get();
}
}

void PlatformCAFilters::presentationModifiers(const FilterOperations& initialFilters, const FilterOperations* canonicalFilters, Vector<TypedFilterPresentationModifier>& presentationModifiers, RetainPtr<CAPresentationModifierGroup>& group)
{
if (!canonicalFilters || canonicalFilters->isEmpty())
return;

ASSERT(canonicalFilters->size() >= initialFilters.size());
ASSERT(presentationModifierCount(*canonicalFilters));

auto& canonicalFilterOperations = canonicalFilters->operations();
auto& initialFilterOperations = initialFilters.operations();
auto numberOfInitialFilters = initialFilterOperations.size();
for (size_t i = 0; i < canonicalFilterOperations.size(); ++i) {
auto& canonicalFilterOperation = *canonicalFilterOperations[i];
auto& initialFilterOperation = i < numberOfInitialFilters ? *initialFilterOperations[i] : passthroughFilter(canonicalFilterOperation.type());
ASSERT(canonicalFilterOperation.type() == initialFilterOperation.type());
auto filterName = makeString("filter_", i);
auto type = initialFilterOperation.type();
switch (type) {
case FilterOperation::Type::Default:
case FilterOperation::Type::Reference:
case FilterOperation::Type::None:
ASSERT_NOT_REACHED();
break;
case FilterOperation::Type::DropShadow: {
const auto& dropShadowOperation = downcast<DropShadowFilterOperation>(initialFilterOperation);
auto size = CGSizeMake(dropShadowOperation.x(), dropShadowOperation.y());
presentationModifiers.append({ type, adoptNS([[CAPresentationModifier alloc] initWithKeyPath:@"shadowOffset" initialValue:[NSValue value:&size withObjCType:@encode(CGSize)] additive:NO group:group.get()]) });
presentationModifiers.append({ type, adoptNS([[CAPresentationModifier alloc] initWithKeyPath:@"shadowColor" initialValue:(id) cachedCGColor(dropShadowOperation.color()).autorelease() additive:NO group:group.get()]) });
presentationModifiers.append({ type, adoptNS([[CAPresentationModifier alloc] initWithKeyPath:@"shadowRadius" initialValue:@(dropShadowOperation.stdDeviation()) additive:NO group:group.get()]) });
continue;
}
case FilterOperation::Type::Grayscale:
case FilterOperation::Type::Sepia:
case FilterOperation::Type::Saturate:
case FilterOperation::Type::HueRotate:
case FilterOperation::Type::Invert:
case FilterOperation::Type::Opacity:
case FilterOperation::Type::Brightness:
case FilterOperation::Type::Contrast:
case FilterOperation::Type::Blur: {
auto keyValueName = makeString("filters.", filterName, ".", animatedFilterPropertyName(initialFilterOperation.type()));
presentationModifiers.append({ type, adoptNS([[CAPresentationModifier alloc] initWithKeyPath:keyValueName initialValue:filterValueForOperation(&initialFilterOperation).get() additive:NO group:group.get()]) });
continue;
}
case FilterOperation::Type::AppleInvertLightness:
ASSERT_NOT_REACHED(); // AppleInvertLightness is only used in -apple-color-filter.
break;
case FilterOperation::Type::Passthrough:
continue;
}
ASSERT_NOT_REACHED();
break;
}

ASSERT(presentationModifierCount(*canonicalFilters) == presentationModifiers.size());
}

void PlatformCAFilters::updatePresentationModifiers(const FilterOperations& filters, const Vector<TypedFilterPresentationModifier>& presentationModifiers)
{
ASSERT(presentationModifierCount(filters) <= presentationModifiers.size());

size_t filterIndex = 0;
auto numberOfFilters = filters.size();
for (size_t i = 0; i < presentationModifiers.size(); ++i) {
auto& filterOperation = filterIndex < numberOfFilters ? *filters.at(filterIndex) : passthroughFilter(presentationModifiers[i].first);
++filterIndex;
switch (filterOperation.type()) {
case FilterOperation::Type::Default:
case FilterOperation::Type::Reference:
case FilterOperation::Type::None:
ASSERT_NOT_REACHED();
return;
case FilterOperation::Type::DropShadow: {
const auto& dropShadowOperation = downcast<DropShadowFilterOperation>(filterOperation);
auto size = CGSizeMake(dropShadowOperation.x(), dropShadowOperation.y());
[presentationModifiers[i].second.get() setValue:[NSValue value:&size withObjCType:@encode(CGSize)]];
[presentationModifiers[i++].second.get() setValue:(id) cachedCGColor(dropShadowOperation.color()).autorelease()];
[presentationModifiers[i++].second.get() setValue:@(dropShadowOperation.stdDeviation())];
continue;
}
case FilterOperation::Type::Grayscale:
case FilterOperation::Type::Sepia:
case FilterOperation::Type::Saturate:
case FilterOperation::Type::HueRotate:
case FilterOperation::Type::Invert:
case FilterOperation::Type::Opacity:
case FilterOperation::Type::Brightness:
case FilterOperation::Type::Contrast:
case FilterOperation::Type::Blur: {
[presentationModifiers[i].second.get() setValue:filterValueForOperation(&filterOperation).get()];
continue;
}
case FilterOperation::Type::AppleInvertLightness:
ASSERT_NOT_REACHED(); // AppleInvertLightness is only used in -apple-color-filter.
return;
case FilterOperation::Type::Passthrough:
continue;
}
ASSERT_NOT_REACHED();
return;
}
}
#endif // PLATFORM(MAC)

void PlatformCAFilters::setFiltersOnLayer(PlatformLayer* layer, const FilterOperations& filters)
{
if (!filters.size()) {
Expand Down Expand Up @@ -101,7 +284,6 @@
const auto& colorMatrixOperation = downcast<BasicColorMatrixFilterOperation>(filterOperation);
CAFilter *filter = [CAFilter filterWithType:kCAFilterColorHueRotate];
[filter setValue:[NSNumber numberWithFloat:deg2rad(colorMatrixOperation.amount())] forKey:@"inputAngle"];
[filter setName:@"hueRotate"];
[filter setName:filterName];
return filter;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
#include <WebCore/AcceleratedEffect.h>
#include <WebCore/AcceleratedEffectStack.h>
#include <WebCore/AcceleratedEffectValues.h>
#include <WebCore/PlatformCAFilters.h>
#include <WebCore/PlatformLayer.h>
#include <wtf/OptionSet.h>
#include <wtf/RetainPtr.h>

OBJC_CLASS CAPresentationModifierGroup;
Expand Down Expand Up @@ -59,19 +61,27 @@ class RemoteAcceleratedEffectStack final : public WebCore::AcceleratedEffectStac

WebCore::AcceleratedEffectValues computeValues(MonotonicTime now) const;

#if PLATFORM(MAC)
const WebCore::FilterOperations* longestFilterList() const;
#endif

enum class LayerProperty : uint8_t {
Opacity = 1 << 1,
Transform = 1 << 2
Transform = 1 << 2,
Filter = 1 << 3
};

OptionSet<LayerProperty> m_affectedLayerProperties;

WebCore::FloatRect m_bounds;
Seconds m_acceleratedTimelineTimeOrigin;

#if PLATFORM(MAC)
RetainPtr<CAPresentationModifierGroup> m_presentationModifierGroup;
RetainPtr<CAPresentationModifier> m_opacityPresentationModifier;
RetainPtr<CAPresentationModifier> m_transformPresentationModifier;
Vector<WebCore::TypedFilterPresentationModifier> m_filterPresentationModifiers;
#endif
};

} // namespace WebKit
Expand Down
Loading

0 comments on commit 7591a30

Please sign in to comment.