| ... | ... | @@ -6,6 +6,38 @@ Rotate [`mpv`][] videos by [arbitrary angles on the GPU][]. | 
| 6 | 6 | [`mpv`]: https://mpv.io | 
| 7 | 7 | [arbitrary angles on the GPU]: https://github.com/mpv-player/mpv/issues/3434#issuecomment-275195855 | 
| 8 | 8 |  | 
| 9 | +## Installation | |
| 10 | + | |
| 11 | +Copy or [symlink][] the following file into the [`mpv` configuration | |
| 12 | +directory][], preserving subdirectories: | |
| 13 | + | |
| 14 | +- [`scripts/gpu_rotate.lua`][] | |
| 15 | + | |
| 16 | +Example usage configuration can be found in: | |
| 17 | + | |
| 18 | +- [`mpv.conf.example`][] | |
| 19 | +- [`input.conf.example`][] | |
| 20 | + | |
| 21 | +On Unix-likes, creating symlinks can be done automatically by [`stow`][] by | |
| 22 | +running the following from the repository root: | |
| 23 | + | |
| 24 | +```sh | |
| 25 | +stow --no-folding --ignore='\.example$' --target="$HOME/.config/mpv" . | |
| 26 | +``` | |
| 27 | + | |
| 28 | +[symlink]: https://en.wikipedia.org/wiki/Symbolic_link | |
| 29 | +[`mpv` configuration directory]: https://mpv.io/manual/master/#files | |
| 30 | +[`scripts/gpu_rotate.lua`]: scripts/gpu_rotate.lua | |
| 31 | +[`mpv.conf.example`]: mpv.conf.example | |
| 32 | +[`input.conf.example`]: input.conf.example | |
| 33 | +[`stow`]: https://www.gnu.org/software/stow/ | |
| 34 | + | |
| 35 | +## Contributing | |
| 36 | + | |
| 37 | +Lint with [`luacheck`][]. | |
| 38 | + | |
| 39 | +[`luacheck`]: https://github.com/mpeterv/luacheck | |
| 40 | + | |
| 9 | 41 | ## License | 
| 10 | 42 |  | 
| 11 | 43 | Licensed under the [ISC License][] unless otherwise noted, see the | 
| 0 | 4 | new file mode 100644 | 
| ... | ... | @@ -0,0 +1,8 @@ | 
| 1 | +# Usually the default. Writes speratate shader files to change rotation angle. | |
| 2 | +# `os.tmpname()` is used to generate the filenames. On some systems this file | |
| 3 | +# is readable by other users, but it contains no user- or video-specific | |
| 4 | +# information, only the rotation angle. | |
| 5 | +vo=gpu | |
| 6 | + | |
| 7 | +# Enable debug printing. | |
| 8 | +# msg-level=gpu_rotate=debug | 
| 0 | 9 | new file mode 100644 | 
| ... | ... | @@ -0,0 +1,75 @@ | 
| 1 | +--- Require | |
| 2 | + | |
| 3 | +local mp = require("mp") | |
| 4 | + | |
| 5 | +--- Constants | |
| 6 | + | |
| 7 | +local SHADER_META = [[ | |
| 8 | +//!HOOK MAINPRESUB | |
| 9 | +//!DESC GPU rotate | |
| 10 | +//!BIND HOOKED | |
| 11 | +]] | |
| 12 | + | |
| 13 | +local SHADER_DEFINE = "#define angle" | |
| 14 | + | |
| 15 | +local SHADER_HOOK = [[ | |
| 16 | +vec4 hook() | |
| 17 | +{ | |
| 18 | + float rad = radians(angle); | |
| 19 | + float c = cos(rad); | |
| 20 | + float s = sin(rad); | |
| 21 | + mat2 rot = mat2(c, -s, s, c); | |
| 22 | + vec2 pos = rot * ((HOOKED_pos - 0.5) * HOOKED_size) * HOOKED_pt + 0.5; | |
| 23 | + if (!all(equal(pos, clamp(pos, 0.0, 1.0)))) | |
| 24 | + return vec4(0.0); | |
| 25 | + return HOOKED_tex(pos); | |
| 26 | +} | |
| 27 | +]] | |
| 28 | + | |
| 29 | +--- State | |
| 30 | + | |
| 31 | +local state = { | |
| 32 | + angle = 0.0, | |
| 33 | + shader = nil, | |
| 34 | +} | |
| 35 | + | |
| 36 | +--- Functions | |
| 37 | + | |
| 38 | +local function shader_define() | |
| 39 | +    return table.concat({SHADER_DEFINE, state.angle}, " ") | |
| 40 | +end | |
| 41 | + | |
| 42 | +local function remove() | |
| 43 | + if state.shader then | |
| 44 | +        mp.msg.debug("Removing", state.shader) | |
| 45 | +        mp.commandv("change-list", "glsl-shaders", "remove", state.shader) | |
| 46 | + os.remove(state.shader) | |
| 47 | + state.shader = nil | |
| 48 | + end | |
| 49 | +end | |
| 50 | + | |
| 51 | +local function append(angle) | |
| 52 | + state.angle = angle % 360 | |
| 53 | + remove() | |
| 54 | + state.shader = os.tmpname() | |
| 55 | +    mp.msg.debug("Writing", state.shader) | |
| 56 | + local file = io.open(state.shader, "w") | |
| 57 | +    file:write(table.concat({ | |
| 58 | + SHADER_META, | |
| 59 | + shader_define(), | |
| 60 | + SHADER_HOOK, | |
| 61 | + }, "\n")) | |
| 62 | + file:close() | |
| 63 | +    mp.commandv("change-list", "glsl-shaders", "append", state.shader) | |
| 64 | +end | |
| 65 | + | |
| 66 | +--- Events | |
| 67 | + | |
| 68 | +mp.register_event("shutdown", remove) | |
| 69 | + | |
| 70 | +--- Script messages | |
| 71 | + | |
| 72 | +local function set(angle) append(angle) end | |
| 73 | +local function add(angle) append(angle + state.angle) end | |
| 74 | +mp.register_script_message("set", set) | |
| 75 | +mp.register_script_message("add", add) |