diff --git a/js/ui/magnifier.js b/js/ui/magnifier.js index 1e972d3c7..d773f080f 100644 --- a/js/ui/magnifier.js +++ b/js/ui/magnifier.js @@ -25,6 +25,7 @@ const SHOW_KEY = 'screen-magnifier-enabled'; const MAGNIFIER_SCHEMA = 'org.gnome.desktop.a11y.magnifier'; const SCREEN_POSITION_KEY = 'screen-position'; const MAG_FACTOR_KEY = 'mag-factor'; +const INVERT_LIGHTNESS_KEY = 'invert-lightness'; const BRIGHT_RED_KEY = 'brightness-red'; const BRIGHT_GREEN_KEY = 'brightness-green'; const BRIGHT_BLUE_KEY = 'brightness-blue'; @@ -451,6 +452,10 @@ const Magnifier = new Lang.Class({ if (aPref) zoomRegion.setMouseTrackingMode(aPref); + aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY); + if (aPref) + zoomRegion.setInvertLightness(aPref); + let bc = {}; bc.r = this._settings.get_double(BRIGHT_RED_KEY); bc.g = this._settings.get_double(BRIGHT_GREEN_KEY); @@ -483,6 +488,9 @@ const Magnifier = new Lang.Class({ this._settings.connect('changed::' + MOUSE_TRACKING_KEY, Lang.bind(this, this._updateMouseTrackingMode)); + this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY, + Lang.bind(this, this._updateInvertLightness)); + this._settings.connect('changed::' + BRIGHT_RED_KEY, Lang.bind(this, this._updateBrightness)); this._settings.connect('changed::' + BRIGHT_GREEN_KEY, @@ -574,6 +582,15 @@ const Magnifier = new Lang.Class({ } }, + _updateInvertLightness: function() { + // Applies only to the first zoom region. + if (this._zoomRegions.length) { + this._zoomRegions[0].setInvertLightness( + this._settings.get_boolean(INVERT_LIGHTNESS_KEY) + ); + } + }, + _updateBrightness: function() { // Applies only to the first zoom region. if (this._zoomRegions.length) { @@ -608,6 +625,7 @@ const ZoomRegion = new Lang.Class({ this._clampScrollingAtEdges = false; this._lensMode = false; this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN; + this._invertLightness = false; this._brightness = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE }; this._contrast = { r: NO_CHANGE, g: NO_CHANGE, b: NO_CHANGE }; @@ -935,6 +953,26 @@ const ZoomRegion = new Lang.Class({ } }, + /** + * setInvertLightness: + * Set whether to invert the lightness of the magnified view. + * @flag Boolean to either invert brightness (true), or not (false). + */ + setInvertLightness: function(flag) { + this._invertLightness = flag; + if (this._magShaderEffects) + this._magShaderEffects.setInvertLightness(this._invertLightness); + }, + + /** + * getInvertLightness: + * Retrieve whether the lightness is inverted. + * @return Boolean indicating inversion (true), or not (false). + */ + getInvertLightness: function() { + return this._invertLightness; + }, + /** * setBrightness: * Alter the brightness of the magnified view. @@ -1036,6 +1074,7 @@ const ZoomRegion = new Lang.Class({ // Contrast and brightness effects. this._magShaderEffects = new MagShaderEffects(this._uiGroupClone); + this._magShaderEffects.setInvertLightness(this._invertLightness); this._magShaderEffects.setBrightness(this._brightness); this._magShaderEffects.setContrast(this._contrast); }, @@ -1561,10 +1600,13 @@ const MagShaderEffects = new Lang.Class({ Name: 'MagShaderEffects', _init: function(uiGroupClone) { + this._inverse = new Shell.InvertLightnessEffect(); this._brightnessContrast = new Clutter.BrightnessContrastEffect(); + this._inverse.set_enabled(false); this._brightnessContrast.set_enabled(false); this._magView = uiGroupClone; + this._magView.add_effect(this._inverse); this._magView.add_effect(this._brightnessContrast); }, @@ -1577,9 +1619,28 @@ const MagShaderEffects = new Lang.Class({ destroyEffects: function() { this._magView.clear_effects(); this._brightnessContrast = null; + this._inverse = null; this._magView = null; }, + /** + * setInvertLightness: + * Enable/disable invert lightness effect. + * @invertFlag: Enabled flag. + */ + setInvertLightness: function(invertFlag) { + this._inverse.set_enabled(invertFlag); + }, + + /** + * getInvertLightness: + * Report whether the inversion effect is enabled. + * @return: Boolean. + */ + getInvertLightness: function() { + return this._inverse.get_enabled(); + }, + /** * setBrightness: * Set the brightness of the magnified view. diff --git a/src/Makefile.am b/src/Makefile.am index f1721fbd6..981a16d4b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -114,6 +114,7 @@ shell_public_headers_h = \ shell-gtk-embed.h \ shell-global.h \ shell-idle-monitor.h \ + shell-invert-lightness-effect.h \ shell-mobile-providers.h \ shell-mount-operation.h \ shell-network-agent.h \ @@ -161,6 +162,7 @@ libgnome_shell_la_SOURCES = \ shell-gtk-embed.c \ shell-global.c \ shell-idle-monitor.c \ + shell-invert-lightness-effect.c \ shell-keyring-prompt.h \ shell-keyring-prompt.c \ shell-mobile-providers.c \ diff --git a/src/shell-invert-lightness-effect.c b/src/shell-invert-lightness-effect.c new file mode 100644 index 000000000..4d6c27e3f --- /dev/null +++ b/src/shell-invert-lightness-effect.c @@ -0,0 +1,214 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2010-2012 Inclusive Design Research Centre, OCAD University. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Joseph Scheuhammer + */ + +/** + * SECTION:shell-invert-lightness-effect + * @short_description: A colorization effect where lightness is inverted but + * color is not. + * @see_also: #ClutterEffect, #ClutterOffscreenEffect + * + * #ShellInvertLightnessEffect is a sub-class of #ClutterEffect that enhances + * the appearance of a clutter actor. Specifically it inverts the lightness + * of a #ClutterActor (e.g., darker colors become lighter, white becomes black, + * and white, black). + */ + +#define SHELL_INVERT_LIGHTNESS_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_INVERT_LIGHTNESS_EFFECT, ShellInvertLightnessEffectClass)) +#define SHELL_IS_INVERT_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_INVERT_LIGHTNESS_EFFECT)) +#define SHELL_INVERT_LIGHTNESS_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_INVERT_LIGHTNESS_EFFEC, ShellInvertLightnessEffectClass)) + +#define CLUTTER_ENABLE_EXPERIMENTAL_API + +#include "shell-invert-lightness-effect.h" + +#include + +struct _ShellInvertLightnessEffect +{ + ClutterOffscreenEffect parent_instance; + + gint tex_width; + gint tex_height; + + CoglPipeline *pipeline; +}; + +struct _ShellInvertLightnessEffectClass +{ + ClutterOffscreenEffectClass parent_class; + + CoglPipeline *base_pipeline; +}; + +/* Lightness inversion in GLSL. + */ +static const gchar *invert_lightness_source = + "cogl_texel = texture2D (cogl_sampler, cogl_tex_coord.st);\n" + "vec3 effect = vec3 (cogl_texel);\n" + "\n" + "float maxColor = max (cogl_texel.r, max (cogl_texel.g, cogl_texel.b));\n" + "float minColor = min (cogl_texel.r, min (cogl_texel.g, cogl_texel.b));\n" + "float lightness = (maxColor + minColor) / 2.0;\n" + "\n" + "float delta = (1.0 - lightness) - lightness;\n" + "effect.rgb = (effect.rgb + delta);\n" + "\n" + "cogl_texel = vec4 (effect, cogl_texel.a);\n"; + +G_DEFINE_TYPE (ShellInvertLightnessEffect, + shell_invert_lightness_effect, + CLUTTER_TYPE_OFFSCREEN_EFFECT); + +static gboolean +shell_invert_lightness_effect_pre_paint (ClutterEffect *effect) +{ + ShellInvertLightnessEffect *self = SHELL_INVERT_LIGHTNESS_EFFECT (effect); + ClutterEffectClass *parent_class; + + if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) + return FALSE; + + if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) + { + /* if we don't have support for GLSL shaders then we + * forcibly disable the ActorMeta + */ + g_warning ("Unable to use the ShellInvertLightnessEffect: the " + "graphics hardware or the current GL driver does not " + "implement support for the GLSL shading language."); + clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (self), FALSE); + return FALSE; + } + + parent_class = + CLUTTER_EFFECT_CLASS (shell_invert_lightness_effect_parent_class); + if (parent_class->pre_paint (effect)) + { + ClutterOffscreenEffect *offscreen_effect = + CLUTTER_OFFSCREEN_EFFECT (effect); + CoglHandle texture; + + texture = clutter_offscreen_effect_get_texture (offscreen_effect); + self->tex_width = cogl_texture_get_width (texture); + self->tex_height = cogl_texture_get_height (texture); + + cogl_pipeline_set_layer_texture (self->pipeline, 0, texture); + + return TRUE; + } + else + return FALSE; +} + +static void +shell_invert_lightness_effect_paint_target (ClutterOffscreenEffect *effect) +{ + ShellInvertLightnessEffect *self = SHELL_INVERT_LIGHTNESS_EFFECT (effect); + ClutterActor *actor; + guint8 paint_opacity; + + actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); + paint_opacity = clutter_actor_get_paint_opacity (actor); + + cogl_pipeline_set_color4ub (self->pipeline, + paint_opacity, + paint_opacity, + paint_opacity, + paint_opacity); + cogl_push_source (self->pipeline); + + cogl_rectangle (0, 0, self->tex_width, self->tex_height); + + cogl_pop_source (); +} + +static void +shell_invert_lightness_effect_dispose (GObject *gobject) +{ + ShellInvertLightnessEffect *self = SHELL_INVERT_LIGHTNESS_EFFECT (gobject); + + if (self->pipeline != NULL) + { + cogl_object_unref (self->pipeline); + self->pipeline = NULL; + } + + G_OBJECT_CLASS (shell_invert_lightness_effect_parent_class)->dispose (gobject); +} + +static void +shell_invert_lightness_effect_class_init (ShellInvertLightnessEffectClass *klass) +{ + ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterOffscreenEffectClass *offscreen_class; + + offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); + offscreen_class->paint_target = shell_invert_lightness_effect_paint_target; + + effect_class->pre_paint = shell_invert_lightness_effect_pre_paint; + + gobject_class->dispose = shell_invert_lightness_effect_dispose; +} + +static void +shell_invert_lightness_effect_init (ShellInvertLightnessEffect *self) +{ + ShellInvertLightnessEffectClass *klass; + klass = SHELL_INVERT_LIGHTNESS_EFFECT_GET_CLASS (self); + + if (G_UNLIKELY (klass->base_pipeline == NULL)) + { + CoglSnippet *snippet; + CoglContext *ctx = + clutter_backend_get_cogl_context (clutter_get_default_backend ()); + + klass->base_pipeline = cogl_pipeline_new (ctx); + + snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, + NULL, + NULL); + cogl_snippet_set_replace (snippet, invert_lightness_source); + cogl_pipeline_add_layer_snippet (klass->base_pipeline, 0, snippet); + cogl_object_unref (snippet); + + cogl_pipeline_set_layer_null_texture (klass->base_pipeline, + 0, /* layer number */ + COGL_TEXTURE_TYPE_2D); + } + + self->pipeline = cogl_pipeline_copy (klass->base_pipeline); +} + +/** + * shell_invert_lightness_effect_new: + * + * Creates a new #ShellInvertLightnessEffect to be used with + * clutter_actor_add_effect() + * + * Return value: (transfer full): the newly created + * #ShellInvertLightnessEffect or %NULL. Use g_object_unref() when done. + */ +ClutterEffect * +shell_invert_lightness_effect_new (void) +{ + return g_object_new (SHELL_TYPE_INVERT_LIGHTNESS_EFFECT, NULL); +} diff --git a/src/shell-invert-lightness-effect.h b/src/shell-invert-lightness-effect.h new file mode 100644 index 000000000..a7bedd5d1 --- /dev/null +++ b/src/shell-invert-lightness-effect.h @@ -0,0 +1,42 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * Copyright © 2010-2012 Inclusive Design Research Centre, OCAD University. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Author: + * Joseph Scheuhammer + */ +#ifndef __SHELL_INVERT_LIGHTNESS_EFFECT_H__ +#define __SHELL_INVERT_LIGHTNESS_EFFECT_H__ + +#define COGL_ENABLE_EXPERIMENTAL_API +#include + +G_BEGIN_DECLS + +#define SHELL_TYPE_INVERT_LIGHTNESS_EFFECT (shell_invert_lightness_effect_get_type ()) +#define SHELL_INVERT_LIGHTNESS_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_INVERT_LIGHTNESS_EFFECT, ShellInvertLightnessEffect)) +#define SHELL_IS_INVERT_LIGHTNESS_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_INVERT_LIGHTNESS_EFFECT)) + +typedef struct _ShellInvertLightnessEffect ShellInvertLightnessEffect; +typedef struct _ShellInvertLightnessEffectClass ShellInvertLightnessEffectClass; + +GType shell_invert_lightness_effect_get_type (void) G_GNUC_CONST; + +ClutterEffect *shell_invert_lightness_effect_new (void); + +G_END_DECLS + +#endif /* __SHELL_INVERT_LIGHTNESS_EFFECT_H__ */