Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(343)

Unified Diff: ui/accelerated_widget_mac/ca_renderer_layer_tree.mm

Issue 2186693002: Implement filter effects in Core Animation using CAFilters. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@temp50_base
Patch Set: Comments from piman. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ui/accelerated_widget_mac/ca_renderer_layer_tree.h ('k') | ui/gl/ca_renderer_layer_params.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
index d5b3734a72676c4e534753b54e55c877b84bdc01..7159a885416d98cc52a745c7f6ff3dae6e2ddd9e 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -16,7 +16,6 @@
#include "ui/base/cocoa/animation_utils.h"
#include "ui/base/ui_base_switches.h"
#include "ui/gfx/geometry/dip_util.h"
-#include "ui/gl/ca_renderer_layer_params.h"
#include "ui/gl/gl_image_io_surface.h"
#if !defined(MAC_OS_X_VERSION_10_8) || \
@@ -46,10 +45,61 @@ extern CFStringRef const kCMSampleAttachmentKey_DisplayImmediately;
extern const CMTime kCMTimeInvalid;
#endif // MAC_OS_X_VERSION_10_8
+// CAFilter and CAColorMatrix are QuartzCore SPI.
+@interface CAFilter : NSObject<NSCopying, NSMutableCopying, NSCoding>
+@end
+
+@interface CAFilter (QuartzCoreSPI)
++ (CAFilter*)filterWithType:(NSString*)type;
+@end
+
+// TODO(erikchen): Test out the named filter kCAFilterColorInvert.
+// https://crbug.com/581526.
+extern NSString* const kCAFilterColorMatrix;
+extern NSString* const kCAFilterColorMonochrome;
+extern NSString* const kCAFilterColorHueRotate;
+extern NSString* const kCAFilterColorSaturate;
+extern NSString* const kCAFilterGaussianBlur;
+
+struct CAColorMatrix {
+ float m11, m12, m13, m14, m15;
+ float m21, m22, m23, m24, m25;
+ float m31, m32, m33, m34, m35;
+ float m41, m42, m43, m44, m45;
+};
+typedef struct CAColorMatrix CAColorMatrix;
+
+@interface NSValue (QuartzCoreSPI)
++ (NSValue*)valueWithCAColorMatrix:(CAColorMatrix)t;
+@end
+
namespace ui {
namespace {
+float Blend(float from, float to, float progress) {
+ return from + (to - from) * progress;
+}
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+double DegreeToRadians(double degree) { return degree * M_PI / 180.0; }
+
+// These values were obtained from https://www.w3.org/TR/filter-effects/.
+static const double kSepiaFullConstants[3][3] = {
+ { 0.393, 0.769, 0.189 },
+ { 0.349, 0.686, 0.168 },
+ { 0.272, 0.534, 0.131 }
+};
+
+static const double kSepiaNoneConstants[3][3] = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 }
+};
+
// This will enqueue |io_surface| to be drawn by |av_layer|. This will
// retain |cv_pixel_buffer| until it is no longer being displayed.
bool AVSampleBufferDisplayLayerEnqueueCVPixelBuffer(
@@ -127,6 +177,161 @@ bool AVSampleBufferDisplayLayerEnqueueIOSurface(
cv_pixel_buffer);
}
+// If the filter effect can be represented as a named filter, return it.
+// Otherwise, return nil.
+CAFilter* NamedFilterForType(CARendererLayerParams::FilterEffectType type,
+ float amount) {
+ CAFilter* filter = nil;
+ switch (type) {
+ case CARendererLayerParams::FilterEffectType::GRAYSCALE:
+ filter = [CAFilter filterWithType:kCAFilterColorMonochrome];
+ [filter setValue:@(amount) forKey:@"inputAmount"];
+ break;
+ case CARendererLayerParams::FilterEffectType::SATURATE:
+ filter = [CAFilter filterWithType:kCAFilterColorSaturate];
+ [filter setValue:@(amount) forKey:@"inputAmount"];
+ break;
+ case CARendererLayerParams::FilterEffectType::HUE_ROTATE:
+ filter = [CAFilter filterWithType:kCAFilterColorHueRotate];
+ [filter setValue:@(DegreeToRadians(amount)) forKey:@"inputAngle"];
+ break;
+ case CARendererLayerParams::FilterEffectType::BLUR:
+ filter = [CAFilter filterWithType:kCAFilterGaussianBlur];
+ [filter setValue:@(amount) forKey:@"inputRadius"];
+ break;
+ default:
+ break;
+ }
+ return filter;
+}
+
+// If the filter effect has a corresponding color matrix, return it. Otherwise,
+// return nil.
+NSValue* ColorMatrixForType(CARendererLayerParams::FilterEffectType type,
+ float amount) {
+ switch (type) {
+ case CARendererLayerParams::FilterEffectType::SEPIA:
+ {
+ float t = std::min(std::max(0.0f, amount), 1.0f);
+ CAColorMatrix colorMatrix = {
+ Blend(kSepiaNoneConstants[0][0], kSepiaFullConstants[0][0], t),
+ Blend(kSepiaNoneConstants[0][1], kSepiaFullConstants[0][1], t),
+ Blend(kSepiaNoneConstants[0][2], kSepiaFullConstants[0][2], t),
+ 0, 0,
+
+ Blend(kSepiaNoneConstants[1][0], kSepiaFullConstants[1][0], t),
+ Blend(kSepiaNoneConstants[1][1], kSepiaFullConstants[1][1], t),
+ Blend(kSepiaNoneConstants[1][2], kSepiaFullConstants[1][2], t),
+ 0, 0,
+
+ Blend(kSepiaNoneConstants[2][0], kSepiaFullConstants[2][0], t),
+ Blend(kSepiaNoneConstants[2][1], kSepiaFullConstants[2][1], t),
+ Blend(kSepiaNoneConstants[2][2], kSepiaFullConstants[2][2], t),
+ 0, 0, 0, 0, 0, 1, 0
+ };
+ return [NSValue valueWithCAColorMatrix:colorMatrix];
+ }
+ case CARendererLayerParams::FilterEffectType::INVERT:
+ {
+ float multiplier = 1 - amount * 2;
+ CAColorMatrix colorMatrix = {
+ multiplier, 0, 0, 0, amount,
+ 0, multiplier, 0, 0, amount,
+ 0, 0, multiplier, 0, amount,
+ 0, 0, 0, 1, 0
+ };
+ return [NSValue valueWithCAColorMatrix:colorMatrix];
+ }
+ case CARendererLayerParams::FilterEffectType::BRIGHTNESS:
+ {
+ CAColorMatrix colorMatrix = {
+ amount, 0, 0, 0, 0,
+ 0, amount, 0, 0, 0,
+ 0, 0, amount, 0, 0,
+ 0, 0, 0, 1, 0
+ };
+ return [NSValue valueWithCAColorMatrix:colorMatrix];
+ }
+ case CARendererLayerParams::FilterEffectType::CONTRAST:
+ {
+ float intercept = -0.5 * amount + 0.5;
+ CAColorMatrix colorMatrix = {
+ amount, 0, 0, 0, intercept,
+ 0, amount, 0, 0, intercept,
+ 0, 0, amount, 0, intercept,
+ 0, 0, 0, 1, 0
+ };
+ return [NSValue valueWithCAColorMatrix:colorMatrix];
+ }
+ case CARendererLayerParams::FilterEffectType::OPACITY:
+ {
+ CAColorMatrix colorMatrix = {
+ 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, amount, 0
+ };
+ return [NSValue valueWithCAColorMatrix:colorMatrix];
+ }
+ default:
+ return nil;
+ }
+}
+
+// Updates the CALayer to accurately represent the given |filter_effects|.
+void UpdateFiltersOnCALayer(
+ const CARendererLayerParams::FilterEffects& filter_effects,
+ CALayer* layer) {
+ if (filter_effects.empty()) {
+ // It's possible that this enables shadow properties, even if there were
+ // none before. That's an implementation detail of Core Animation.
+ [layer setShadowOffset:CGSizeZero];
+ [layer setShadowColor:nil];
+ [layer setShadowRadius:0];
+ [layer setShadowOpacity:0];
+ layer.filters = @[];
+ return;
+ }
+
+ NSMutableArray* filters = [NSMutableArray array];
+ for (const CARendererLayerParams::FilterEffect& filter_effect :
+ filter_effects) {
+ CAFilter* filter =
+ NamedFilterForType(filter_effect.type, filter_effect.amount);
+ if (filter) {
+ [filters addObject:filter];
+ continue;
+ }
+
+ NSValue* color_matrix =
+ ColorMatrixForType(filter_effect.type, filter_effect.amount);
+ if (color_matrix) {
+ CAFilter* filter = [CAFilter filterWithType:kCAFilterColorMatrix];
+ [filter setValue:color_matrix forKey:@"inputColorMatrix"];
+ [filters addObject:filter];
+ continue;
+ }
+
+ DCHECK_EQ(CARendererLayerParams::FilterEffectType::DROP_SHADOW,
+ filter_effect.type);
+ [layer setShadowOffset:CGSizeMake(filter_effect.drop_shadow_offset.x(),
+ filter_effect.drop_shadow_offset.y())];
+
+ CGFloat rgba_color_components[4] = {
+ SkColorGetR(filter_effect.drop_shadow_color) / 255.,
+ SkColorGetG(filter_effect.drop_shadow_color) / 255.,
+ SkColorGetB(filter_effect.drop_shadow_color) / 255.,
+ SkColorGetA(filter_effect.drop_shadow_color) / 255.,
+ };
+ base::ScopedCFTypeRef<CGColorRef> srgb_color(CGColorCreate(
+ CGColorSpaceCreateWithName(kCGColorSpaceSRGB), rgba_color_components));
+ [layer setShadowColor:srgb_color.get()];
+ [layer setShadowRadius:filter_effect.amount];
+ [layer setShadowOpacity:1];
+ }
+ layer.filters = filters;
+}
+
} // namespace
CARendererLayerTree::CARendererLayerTree() {}
@@ -274,7 +479,8 @@ CARendererLayerTree::ContentLayer::ContentLayer(
unsigned background_color,
unsigned edge_aa_mask,
float opacity,
- unsigned filter)
+ unsigned filter,
+ const CARendererLayerParams::FilterEffects& filter_effects)
: io_surface(io_surface),
cv_pixel_buffer(cv_pixel_buffer),
contents_rect(contents_rect),
@@ -282,7 +488,8 @@ CARendererLayerTree::ContentLayer::ContentLayer(
background_color(background_color),
ca_edge_aa_mask(0),
opacity(opacity),
- ca_filter(filter == GL_LINEAR ? kCAFilterLinear : kCAFilterNearest) {
+ ca_filter(filter == GL_LINEAR ? kCAFilterLinear : kCAFilterNearest),
+ filter_effects(filter_effects) {
DCHECK(filter == GL_LINEAR || filter == GL_NEAREST);
// Because the root layer has setGeometryFlipped:YES, there is some ambiguity
@@ -329,7 +536,8 @@ CARendererLayerTree::ContentLayer::ContentLayer(ContentLayer&& layer)
ca_filter(layer.ca_filter),
ca_layer(std::move(layer.ca_layer)),
av_layer(std::move(layer.av_layer)),
- use_av_layer(layer.use_av_layer) {
+ use_av_layer(layer.use_av_layer),
+ filter_effects(layer.filter_effects) {
DCHECK(!layer.ca_layer);
DCHECK(!layer.av_layer);
}
@@ -407,7 +615,7 @@ void CARendererLayerTree::TransformLayer::AddContentLayer(
content_layers.push_back(
ContentLayer(io_surface, cv_pixel_buffer, params.contents_rect,
params.rect, params.background_color, params.edge_aa_mask,
- params.opacity, params.filter));
+ params.opacity, params.filter, params.filter_effects));
}
void CARendererLayerTree::RootLayer::CommitToCA(CALayer* superlayer,
@@ -535,6 +743,9 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer,
bool update_ca_edge_aa_mask = true;
bool update_opacity = true;
bool update_ca_filter = true;
+ bool update_filter_effects =
+ (old_layer && !old_layer->filter_effects.empty()) ||
+ !filter_effects.empty();
if (old_layer && old_layer->use_av_layer == use_av_layer) {
DCHECK(old_layer->ca_layer);
std::swap(ca_layer, old_layer->ca_layer);
@@ -547,6 +758,7 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer,
update_ca_edge_aa_mask = old_layer->ca_edge_aa_mask != ca_edge_aa_mask;
update_opacity = old_layer->opacity != opacity;
update_ca_filter = old_layer->ca_filter != ca_filter;
+ update_filter_effects = old_layer->filter_effects != filter_effects;
} else {
if (use_av_layer) {
av_layer.reset([[AVSampleBufferDisplayLayer alloc] init]);
@@ -562,7 +774,7 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer,
bool update_anything = update_contents || update_contents_rect ||
update_rect || update_background_color ||
update_ca_edge_aa_mask || update_opacity ||
- update_ca_filter;
+ update_ca_filter || update_filter_effects;
if (use_av_layer) {
if (update_contents) {
if (cv_pixel_buffer) {
@@ -606,6 +818,9 @@ void CARendererLayerTree::ContentLayer::CommitToCA(CALayer* superlayer,
[ca_layer setMagnificationFilter:ca_filter];
[ca_layer setMinificationFilter:ca_filter];
}
+ if (update_filter_effects) {
+ UpdateFiltersOnCALayer(filter_effects, ca_layer.get());
+ }
static bool show_borders = base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kShowMacOverlayBorders);
« no previous file with comments | « ui/accelerated_widget_mac/ca_renderer_layer_tree.h ('k') | ui/gl/ca_renderer_layer_params.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698