gaussian_blur.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import GObject from 'gi://GObject';
  2. import * as utils from '../conveniences/utils.js';
  3. const St = await utils.import_in_shell_only('gi://St');
  4. const Shell = await utils.import_in_shell_only('gi://Shell');
  5. const Clutter = await utils.import_in_shell_only('gi://Clutter');
  6. const SHADER_FILENAME = 'gaussian_blur.glsl';
  7. const DEFAULT_PARAMS = {
  8. radius: 30, brightness: .6,
  9. width: 0, height: 0, direction: 0, chained_effect: null
  10. };
  11. export const GaussianBlurEffect = utils.IS_IN_PREFERENCES ?
  12. { default_params: DEFAULT_PARAMS } :
  13. new GObject.registerClass({
  14. GTypeName: "GaussianBlurEffect",
  15. Properties: {
  16. 'radius': GObject.ParamSpec.double(
  17. `radius`,
  18. `Radius`,
  19. `Blur radius`,
  20. GObject.ParamFlags.READWRITE,
  21. 0.0, 2000.0,
  22. 30.0,
  23. ),
  24. 'brightness': GObject.ParamSpec.double(
  25. `brightness`,
  26. `Brightness`,
  27. `Blur brightness`,
  28. GObject.ParamFlags.READWRITE,
  29. 0.0, 1.0,
  30. 0.6,
  31. ),
  32. 'width': GObject.ParamSpec.double(
  33. `width`,
  34. `Width`,
  35. `Width`,
  36. GObject.ParamFlags.READWRITE,
  37. 0.0, Number.MAX_SAFE_INTEGER,
  38. 0.0,
  39. ),
  40. 'height': GObject.ParamSpec.double(
  41. `height`,
  42. `Height`,
  43. `Height`,
  44. GObject.ParamFlags.READWRITE,
  45. 0.0, Number.MAX_SAFE_INTEGER,
  46. 0.0,
  47. ),
  48. 'direction': GObject.ParamSpec.int(
  49. `direction`,
  50. `Direction`,
  51. `Direction`,
  52. GObject.ParamFlags.READWRITE,
  53. 0, 1,
  54. 0,
  55. ),
  56. 'chained_effect': GObject.ParamSpec.object(
  57. `chained_effect`,
  58. `Chained Effect`,
  59. `Chained Effect`,
  60. GObject.ParamFlags.READWRITE,
  61. GObject.Object,
  62. ),
  63. }
  64. }, class GaussianBlurEffect extends Clutter.ShaderEffect {
  65. constructor(params) {
  66. super(params);
  67. utils.setup_params(this, params);
  68. // set shader source
  69. this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
  70. if (this._source)
  71. this.set_shader_source(this._source);
  72. const theme_context = St.ThemeContext.get_for_stage(global.stage);
  73. theme_context.connectObject(
  74. 'notify::scale-factor', _ =>
  75. this.set_uniform_value('sigma',
  76. parseFloat(this.radius * theme_context.scale_factor / 2 - 1e-6)
  77. ),
  78. this
  79. );
  80. }
  81. static get default_params() {
  82. return DEFAULT_PARAMS;
  83. }
  84. get radius() {
  85. return this._radius;
  86. }
  87. set radius(value) {
  88. if (this._radius !== value) {
  89. this._radius = value;
  90. const scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
  91. // like Clutter, we use the assumption radius = 2*sigma
  92. this.set_uniform_value('sigma', parseFloat(this._radius * scale_factor / 2 - 1e-6));
  93. this.set_enabled(this.radius > 0.);
  94. if (this.chained_effect)
  95. this.chained_effect.radius = value;
  96. }
  97. }
  98. get brightness() {
  99. return this._brightness;
  100. }
  101. set brightness(value) {
  102. if (this._brightness !== value) {
  103. this._brightness = value;
  104. this.set_uniform_value('brightness', parseFloat(this._brightness - 1e-6));
  105. if (this.chained_effect)
  106. this.chained_effect.brightness = value;
  107. }
  108. }
  109. get width() {
  110. return this._width;
  111. }
  112. set width(value) {
  113. if (this._width !== value) {
  114. this._width = value;
  115. this.set_uniform_value('width', parseFloat(this._width + 3.0 - 1e-6));
  116. if (this.chained_effect)
  117. this.chained_effect.width = value;
  118. }
  119. }
  120. get height() {
  121. return this._height;
  122. }
  123. set height(value) {
  124. if (this._height !== value) {
  125. this._height = value;
  126. this.set_uniform_value('height', parseFloat(this._height + 3.0 - 1e-6));
  127. if (this.chained_effect)
  128. this.chained_effect.height = value;
  129. }
  130. }
  131. get direction() {
  132. return this._direction;
  133. }
  134. set direction(value) {
  135. if (this._direction !== value)
  136. this._direction = value;
  137. }
  138. get chained_effect() {
  139. return this._chained_effect;
  140. }
  141. set chained_effect(value) {
  142. this._chained_effect = value;
  143. }
  144. vfunc_set_actor(actor) {
  145. if (this._actor_connection_size_id) {
  146. let old_actor = this.get_actor();
  147. old_actor?.disconnect(this._actor_connection_size_id);
  148. }
  149. if (actor) {
  150. this.width = actor.width;
  151. this.height = actor.height;
  152. this._actor_connection_size_id = actor.connect('notify::size', _ => {
  153. this.width = actor.width;
  154. this.height = actor.height;
  155. });
  156. }
  157. else
  158. this._actor_connection_size_id = null;
  159. super.vfunc_set_actor(actor);
  160. if (this.direction == 0) {
  161. if (this.chained_effect)
  162. this.chained_effect.get_actor()?.remove_effect(this.chained_effect);
  163. else
  164. this.chained_effect = new GaussianBlurEffect({
  165. radius: this.radius,
  166. brightness: this.brightness,
  167. width: this.width,
  168. height: this.height,
  169. direction: 1
  170. });
  171. if (actor !== null)
  172. actor.add_effect(this.chained_effect);
  173. }
  174. }
  175. vfunc_paint_target(paint_node, paint_context) {
  176. this.set_uniform_value("dir", this.direction);
  177. super.vfunc_paint_target(paint_node, paint_context);
  178. }
  179. });