Skip to content

Feature: Create Magnifier Shader #274

@chiefcll

Description

@chiefcll

We have an accessibility requirement to magnify a portion of the screen for visual impaired users. I've started working on a magnifier shader, but GLSL + algebra is going to take me a while. I'd like for a magnifier shader to be part of the shaders included with Lightning. I've attached what I have so far.. I'm sure it can be made better + fancier by someone who knows what they are doing.

Screen.Recording.2021-01-06.at.12.27.43.PM.mov
import DefaultShader from 'wpe-lightning/src/renderer/webgl/shaders/DefaultShader';
export default class MagnifierShader extends DefaultShader {
  constructor(context) {
    super(context);
    this._x = 0;
    this._y = 0;
    this._w = 0;
    this._h = 0;
    this._radius = 0;
    this._magnification = 0.6;
  }

  get x() {
    return this._x;
  }

  set x(v) {
    this._x = v;
    this.redraw();
  }

  get y() {
    return this._y;
  }

  set y(v) {
    this._y = v;
    this.redraw();
  }

  get w() {
    return this._w;
  }

  set w(v) {
    this._w = v;
    this.redraw();
  }

  get h() {
    return this._h;
  }

  set h(v) {
    this._h = v;
    this.redraw();
  }

  set a(v) {
    this._a = v;
    this.redraw();
  }

  get a() {
    return this._a;
  }

  get magnification() {
    return this._magnification;
  }

  set magnification(v) {
    this._magnification = v;
    this.redraw();
  }

  get radius() {
    return this._radius;
  }

  set radius(v) {
    this._radius = v;
    this.redraw();
  }

  setupUniforms(operation) {
    super.setupUniforms(operation);

    const owner = operation.shaderOwner;
    const renderPrecision = this.ctx.stage.getRenderPrecision();
    this._setUniform('x', this._x * renderPrecision, this.gl.uniform1f);
    this._setUniform('y', this._y * renderPrecision, this.gl.uniform1f);
    this._setUniform('w', this._w * renderPrecision, this.gl.uniform1f);
    this._setUniform('h', this._h * renderPrecision, this.gl.uniform1f);
    this._setUniform('magnification', this._magnification, this.gl.uniform1f);
    this._setUniform('a', this._a, this.gl.uniform1f);
    this._setUniform(
      'radius',
      (this._radius + 0.5) * renderPrecision,
      this.gl.uniform1f
    );
    this._setUniform(
      'resolution',
      new Float32Array([
        owner._w * renderPrecision,
        owner._h * renderPrecision
      ]),
      this.gl.uniform2fv
    );
  }

  beforeDraw() {
    this.gl.enable(this.gl.BLEND);
    this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
  }

  afterDraw() {
    this.gl.disable(this.gl.BLEND);
  }

  useDefault() {
    return this._x === 0 && this._y === 0 && this._w === 0 && this._h === 0;
  }
}

MagnifierShader.vertexShaderSource = DefaultShader.vertexShaderSource;

MagnifierShader.fragmentShaderSource = `
    #ifdef GL_ES
      # ifdef GL_FRAGMENT_PRECISION_HIGH
      precision highp float;
      # else
      precision lowp float;
      # endif
    #endif
    varying vec2 vTextureCoord;
    varying vec4 vColor;
    uniform sampler2D uSampler;
    uniform float x;
    uniform float y;
    uniform float w;
    uniform float h;
    uniform vec2 resolution;
    uniform float radius;
    uniform float magnification;
    uniform float a;

    float roundBox(vec2 p, vec2 b, float r) {
        float d = length(max(abs(p)-b+r, 0.1))-r;
        return smoothstep(1.0, 0.0, d);
    }

    void main(void) {
      vec4 color = texture2D(uSampler, vTextureCoord);
      vec2 pos = vTextureCoord.xy * resolution - vec2(x, y) - vec2(w, h) / 2.0;
      vec2 size = vec2(w, h) / 2.0;
      float b = roundBox(pos, size, radius);
      vec2 pos2 = (vTextureCoord.xy * magnification * resolution + vec2(x, y) * magnification) / resolution;
      gl_FragColor = mix(color, texture2D(uSampler, pos2), b);
    }
`;

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions