├── .github ├── CODEOWNERS ├── FUNDING.yml ├── scripts │ ├── utils.zsh │ │ ├── mkcd │ │ ├── log_error │ │ ├── log_warning │ │ ├── log_debug │ │ ├── log_output │ │ ├── log_status │ │ ├── log_info │ │ ├── read_codesign │ │ ├── read_codesign_user │ │ ├── read_codesign_installer │ │ ├── setup_ccache │ │ ├── set_loglevel │ │ ├── check_macos │ │ ├── check_linux │ │ ├── read_codesign_pass │ │ ├── setup_linux │ │ ├── check_packages │ │ ├── setup_obs │ │ └── setup_macos │ ├── .Brewfile │ ├── .Wingetfile │ ├── .Aptfile │ ├── check-changes.sh │ ├── build-linux.sh │ ├── package-linux.sh │ ├── utils.pwsh │ │ ├── Check-Git.ps1 │ │ ├── Ensure-Location.ps1 │ │ ├── Invoke-External.ps1 │ │ ├── Install-BuildDependencies.ps1 │ │ ├── Expand-ArchiveExt.ps1 │ │ ├── Logger.ps1 │ │ ├── Setup-Obs.ps1 │ │ ├── Invoke-GitCheckout.ps1 │ │ └── Setup-Host.ps1 │ ├── check-cmake.sh │ ├── check-format.sh │ ├── Package-Windows.ps1 │ ├── Build-Windows.ps1 │ ├── .package.zsh │ ├── package-linux.zsh │ └── package-macos.zsh ├── assets │ └── images │ │ ├── star.png │ │ ├── circle.png │ │ ├── ellipse.png │ │ ├── heart.png │ │ ├── rectangle.png │ │ ├── source-mask.png │ │ ├── gradient-mask.png │ │ └── regular-polygon.png ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── actions │ ├── build-plugin │ └── action.yml │ └── package-plugin │ └── action.yml ├── media ├── icon.ico └── logo.png ├── src ├── utils.h ├── version.h ├── version.h.in ├── svg-utils.hpp ├── utils.c ├── advanced-masks.h ├── obs-utils.h ├── color-adjustments.h ├── obs-advanced-masks-plugin.c ├── mask-feather.h ├── advanced-masks-filter.h ├── mask-gradient.h ├── base-filter.h ├── mask-bsm.h ├── mask-chroma-key.h ├── svg-utils.cpp ├── mask-svg.h ├── obs-utils.c ├── color-adjustments.c └── mask-source.h ├── .gitignore ├── cmake └── Bundle │ └── macos │ ├── entitlements.plist │ └── Plugin-Info.plist.in ├── .cmake-format.json ├── resource.rc.in ├── data ├── shaders │ ├── render_output.effect │ ├── feather-mask.effect │ ├── common.effect │ ├── super-key.effect │ ├── jump-flood.effect │ ├── boom-so-much-mask.effect │ ├── svg-mask.effect │ ├── chroma-key.effect │ ├── circle-mask.effect │ ├── heart-mask.effect │ ├── polygon-mask.effect │ ├── superformula-mask.effect │ ├── star-mask.effect │ ├── rectangular-mask.effect │ ├── gradient-mask.effect │ └── ellipse-mask.effect └── locale │ └── es-ES.ini ├── .clang-format ├── installer.iss.in └── CMakeLists.txt /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | /.github/ @FiniteSingularity 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: finitesingularity 2 | patreon: FiniteSingularity 3 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/mkcd: -------------------------------------------------------------------------------- 1 | [[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1} 2 | -------------------------------------------------------------------------------- /media/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/media/icon.ico -------------------------------------------------------------------------------- /media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/media/logo.png -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_error: -------------------------------------------------------------------------------- 1 | local icon=' ✖︎ ' 2 | 3 | print -u2 -PR "%F{1} ${icon} %f ${@}" 4 | -------------------------------------------------------------------------------- /.github/scripts/.Brewfile: -------------------------------------------------------------------------------- 1 | brew "ccache" 2 | brew "coreutils" 3 | brew "cmake" 4 | brew "git" 5 | brew "jq" 6 | brew "ninja" 7 | -------------------------------------------------------------------------------- /.github/assets/images/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/.github/assets/images/star.png -------------------------------------------------------------------------------- /.github/assets/images/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/.github/assets/images/circle.png -------------------------------------------------------------------------------- /.github/assets/images/ellipse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/.github/assets/images/ellipse.png -------------------------------------------------------------------------------- /.github/assets/images/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/.github/assets/images/heart.png -------------------------------------------------------------------------------- /.github/assets/images/rectangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/.github/assets/images/rectangle.png -------------------------------------------------------------------------------- /.github/assets/images/source-mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/.github/assets/images/source-mask.png -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_warning: -------------------------------------------------------------------------------- 1 | if (( _loglevel > 0 )) { 2 | local icon=' =>' 3 | 4 | print -PR "%F{3} ${(r:5:)icon} ${@}%f" 5 | } 6 | -------------------------------------------------------------------------------- /.github/assets/images/gradient-mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/.github/assets/images/gradient-mask.png -------------------------------------------------------------------------------- /.github/assets/images/regular-polygon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiniteSingularity/obs-advanced-masks/HEAD/.github/assets/images/regular-polygon.png -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_debug: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 2 )) print -PR -e -- "%F{220}DEBUG: ${@}%f" 4 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern uint32_t next_power_of_2(uint32_t n); 6 | extern uint32_t previous_power_of_2(uint32_t n); 7 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PROJECT_VERSION "1.5.4" 4 | #define PROJECT_VERSION_MAJOR 1 5 | #define PROJECT_VERSION_MINOR 5 6 | #define PROJECT_VERSION_PATCH 4 7 | -------------------------------------------------------------------------------- /.github/scripts/.Wingetfile: -------------------------------------------------------------------------------- 1 | package '7zip.7zip', path: '7-zip', bin: '7z' 2 | package 'cmake', path: 'Cmake\bin', bin: 'cmake' 3 | package 'innosetup', path: 'Inno Setup 6', bin: 'iscc' 4 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_output: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon='' 5 | 6 | print -PR " ${(r:5:)icon} ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_status: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' >' 5 | 6 | print -PR "%F{2} ${(r:5:)icon}%f ${@}" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/log_info: -------------------------------------------------------------------------------- 1 | if (( ! ${+_loglevel} )) typeset -g _loglevel=1 2 | 3 | if (( _loglevel > 0 )) { 4 | local icon=' =>' 5 | 6 | print -PR "%F{4} ${(r:5:)icon}%f %B${@}%b" 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/.Aptfile: -------------------------------------------------------------------------------- 1 | package 'cmake' 2 | package 'ccache' 3 | package 'curl' 4 | package 'git' 5 | package 'jq' 6 | package 'ninja-build', bin: 'ninja' 7 | package 'pkg-config' 8 | package 'clang' 9 | package 'clang-format-13' 10 | -------------------------------------------------------------------------------- /src/version.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PROJECT_VERSION "${PROJECT_VERSION}" 4 | #define PROJECT_VERSION_MAJOR ${PROJECT_VERSION_MAJOR} 5 | #define PROJECT_VERSION_MINOR ${PROJECT_VERSION_MINOR} 6 | #define PROJECT_VERSION_PATCH ${PROJECT_VERSION_PATCH} 7 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT} )) { 4 | typeset -g CODESIGN_IDENT 5 | log_info 'Setting up identity for application codesigning...' 6 | read CODESIGN_IDENT'?Apple Developer Application ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_user: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT_USER} )) { 4 | typeset -g CODESIGN_IDENT_USER 5 | log_info 'Setting up developer id for codesigning...' 6 | read CODESIGN_IDENT_USER'?Apple Developer ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /src/svg-utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern "C" gs_texture_t* gs_texture_from_svg_path(const char* path, int width, int height, int scale_by); 6 | extern "C" gs_texture_t* gs_texture_from_svg(const char* svg, int width, int height, int scale_by); 7 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_installer: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info 2 | 3 | if (( ! ${+CODESIGN_IDENT_INSTALLER} )) { 4 | typeset -g CODESIGN_IDENT_INSTALLER 5 | log_info 'Setting up identity for installer package codesigning...' 6 | read CODESIGN_IDENT_INSTALLER'?Apple Developer Installer ID: ' 7 | } 8 | -------------------------------------------------------------------------------- /.github/scripts/check-changes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | dirty=$(git ls-files --modified) 3 | 4 | set +x 5 | if [[ $dirty ]]; then 6 | echo "=================================" 7 | echo "Files were not formatted properly" 8 | echo "$dirty" 9 | echo "=================================" 10 | exit 1 11 | fi -------------------------------------------------------------------------------- /.github/scripts/build-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if ! type zsh > /dev/null 2>&1; then 4 | echo ' => Installing script dependency Zsh.' 5 | 6 | sudo apt-get -y update 7 | sudo apt-get -y install zsh 8 | fi 9 | 10 | SCRIPT=$(readlink -f "${0}") 11 | SCRIPT_DIR=$(dirname "${SCRIPT}") 12 | 13 | zsh ${SCRIPT_DIR}/build-linux.zsh "${@}" 14 | -------------------------------------------------------------------------------- /.github/scripts/package-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if ! type zsh > /dev/null 2>&1; then 4 | echo ' => Installing script dependency Zsh.' 5 | 6 | sudo apt-get update 7 | sudo apt-get install zsh 8 | fi 9 | 10 | SCRIPT=$(readlink -f "${0}") 11 | SCRIPT_DIR=$(dirname "${SCRIPT}") 12 | 13 | zsh ${SCRIPT_DIR}/package-linux.zsh "${@}" 14 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/setup_ccache: -------------------------------------------------------------------------------- 1 | autoload -Uz log_debug log_warning 2 | 3 | if (( ${+commands[ccache]} )) { 4 | log_debug "Found ccache at ${commands[ccache]}" 5 | 6 | if (( ${+CI} )) { 7 | ccache --set-config=cache_dir="${GITHUB_WORKSPACE:-${HOME}}/.ccache" 8 | ccache --set-config=max_size="${CCACHE_SIZE:-500M}" 9 | ccache --set-config=compression=true 10 | ccache -z > /dev/null 11 | } 12 | } else { 13 | log_warning "No ccache found on the system" 14 | } 15 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/set_loglevel: -------------------------------------------------------------------------------- 1 | autoload -Uz log_debug log_error 2 | 3 | local -r _usage="Usage: %B${0}%b 4 | 5 | Set log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)" 6 | 7 | if (( ! # )); then 8 | log_error 'Called without arguments.' 9 | log_output ${_usage} 10 | return 2 11 | elif (( ${1} >= 4 )); then 12 | log_error 'Called with loglevel > 3.' 13 | log_output ${_usage} 14 | fi 15 | 16 | typeset -g -i -r _loglevel=${1} 17 | log_debug "Log level set to '${1}'" 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Exclude everything 2 | /* 3 | 4 | # Except for default project files 5 | !/.github 6 | !/build-aux 7 | !/cmake 8 | !/data 9 | !/src 10 | !/media 11 | !.clang-format 12 | !.cmake-format.json 13 | !.gitignore 14 | !.all-contributorsrc 15 | !buildspec.json 16 | !CMakeLists.txt 17 | !CMakePresets.json 18 | !LICENSE 19 | !README.md 20 | !installer.iss.in 21 | !resource.rc.in 22 | 23 | # Exclude lock files 24 | *.lock.json 25 | 26 | # Exclude macOS legacy resource forks 27 | .DS_Store 28 | 29 | # Exclude CMake build number cache 30 | /cmake/.CMakeBuildNumber 31 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | uint32_t next_power_of_2(uint32_t n) { 4 | if (n == 0) 5 | return 1; 6 | // If already a power of 2, return n 7 | if ((n & (n - 1)) == 0) 8 | return n; 9 | 10 | // Else find the next power of 2 11 | uint32_t power = 1; 12 | while (power < n) { 13 | power <<= 1; // Same as power *= 2 14 | } 15 | return power; 16 | } 17 | 18 | uint32_t previous_power_of_2(uint32_t n) { 19 | if (n == 0) 20 | return 0; 21 | 22 | uint32_t power = 1; 23 | while (power << 1 <= n) { 24 | power <<= 1; 25 | } 26 | return power; 27 | } 28 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_macos: -------------------------------------------------------------------------------- 1 | autoload -Uz is-at-least log_info log_error log_status read_codesign 2 | 3 | local macos_version=$(sw_vers -productVersion) 4 | 5 | log_info 'Checking macOS version...' 6 | if ! is-at-least 11.0 "${macos_version}"; then 7 | log_error "Minimum required macOS version is 11.0, but running on macOS ${macos_version}" 8 | return 2 9 | else 10 | log_status "macOS ${macos_version} is recent" 11 | fi 12 | 13 | log_info 'Checking for Homebrew...' 14 | if (( ! ${+commands[brew]} )) { 15 | log_error 'No Homebrew command found. Please install Homebrew (https://brew.sh)' 16 | return 2 17 | } 18 | 19 | brew bundle --file "${SCRIPT_HOME}/.Brewfile" 20 | rehash 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE] - Feature Title Here" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /cmake/Bundle/macos/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | com.apple.security.device.camera 8 | 9 | com.apple.security.device.audio-input 10 | 11 | com.apple.security.cs.disable-library-validation 12 | 13 | 14 | com.apple.security.cs.allow-dyld-environment-variables 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Check-Git.ps1: -------------------------------------------------------------------------------- 1 | function Check-Git { 2 | <# 3 | .SYNOPSIS 4 | Ensures available git executable on host system. 5 | .DESCRIPTION 6 | Checks whether a git command is available on the host system. If none is found, 7 | Git is installed via winget. 8 | .EXAMPLE 9 | Check-Git 10 | #> 11 | 12 | if ( ! ( Test-Path function:Log-Info ) ) { 13 | . $PSScriptRoot/Logger.ps1 14 | } 15 | 16 | Log-Information 'Checking for Git executable...' 17 | 18 | if ( ! ( Get-Command git ) ) { 19 | Log-Warning 'No Git executable found. Will try to install via winget.' 20 | winget install git 21 | } else { 22 | Log-Debug "Git found at $(Get-Command git)." 23 | Log-Status "Git found." 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Ensure-Location.ps1: -------------------------------------------------------------------------------- 1 | function Ensure-Location { 2 | <# 3 | .SYNOPSIS 4 | Ensures current location to be set to specified directory. 5 | .DESCRIPTION 6 | If specified directory exists, switch to it. Otherwise create it, 7 | then switch. 8 | .EXAMPLE 9 | Ensure-Location "My-Directory" 10 | Ensure-Location -Path "Path-To-My-Directory" 11 | #> 12 | 13 | param( 14 | [Parameter(Mandatory)] 15 | [string] $Path 16 | ) 17 | 18 | if ( ! ( Test-Path $Path ) ) { 19 | $_Params = @{ 20 | ItemType = "Directory" 21 | Path = ${Path} 22 | ErrorAction = "SilentlyContinue" 23 | } 24 | 25 | New-Item @_Params | Set-Location 26 | } else { 27 | Set-Location -Path ${Path} 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/advanced-masks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "base-filter.h" 5 | #include "color-adjustments.h" 6 | #include "mask-source.h" 7 | #include "mask-shape.h" 8 | #include "mask-gradient.h" 9 | #include "mask-bsm.h" 10 | #include "mask-chroma-key.h" 11 | #include "mask-feather.h" 12 | #include "mask-svg.h" 13 | 14 | struct advanced_masks_data; 15 | typedef struct advanced_masks_data advanced_masks_data_t; 16 | 17 | struct advanced_masks_data { 18 | base_filter_data_t *base; 19 | color_adjustments_data_t *color_adj_data; 20 | mask_source_data_t *source_data; 21 | mask_shape_data_t *shape_data; 22 | mask_gradient_data_t *gradient_data; 23 | mask_bsm_data_t *bsm_data; 24 | mask_chroma_key_data_t* chroma_key_data; 25 | mask_feather_data_t* feather_data; 26 | mask_svg_data_t* svg_data; 27 | void* font_awesome_data; 28 | 29 | bool invert; 30 | bool multiPassShader; 31 | }; 32 | -------------------------------------------------------------------------------- /.cmake-format.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": { 3 | "line_width": 120, 4 | "tab_size": 2, 5 | "enable_sort": true, 6 | "autosort": true 7 | }, 8 | "additional_commands": { 9 | "find_qt": { 10 | "flags": [], 11 | "kwargs": { 12 | "COMPONENTS": "+", 13 | "COMPONENTS_WIN": "+", 14 | "COMPONENTS_MACOS": "+", 15 | "COMPONENTS_LINUX": "+" 16 | } 17 | }, 18 | "set_target_properties_obs": { 19 | "pargs": 1, 20 | "flags": [], 21 | "kwargs": { 22 | "PROPERTIES": { 23 | "kwargs": { 24 | "PREFIX": 1, 25 | "OUTPUT_NAME": 1, 26 | "FOLDER": 1, 27 | "VERSION": 1, 28 | "SOVERSION": 1, 29 | "AUTOMOC": 1, 30 | "AUTOUIC": 1, 31 | "AUTORCC": 1, 32 | "AUTOUIC_SEARCH_PATHS": 1, 33 | "BUILD_RPATH": 1, 34 | "INSTALL_RPATH": 1 35 | } 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG] - Bug Title Here" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Environment** 27 | - OS/Version: [e.g. Windows 11] 28 | - OBS Version: [e.g. 29.1.2] 29 | - Composite Blur Plugin Version: [e.g. 0.0.1-alpha] 30 | - Blur Algorithm/Type: [e.g. Gaussian, Area] 31 | 32 | **GPU** 33 | - GPU make/model [e.g.- nvidia RTX 3800] 34 | - GPU VRAM [e.g.- 10Gb] 35 | 36 | **Additional context** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /cmake/Bundle/macos/Plugin-Info.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | ${MACOSX_PLUGIN_BUNDLE_NAME} 7 | CFBundleIdentifier 8 | ${MACOSX_PLUGIN_GUI_IDENTIFIER} 9 | CFBundleVersion 10 | ${MACOSX_PLUGIN_BUNDLE_VERSION} 11 | CFBundleShortVersionString 12 | ${MACOSX_PLUGIN_SHORT_VERSION_STRING} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleExecutable 16 | ${MACOSX_PLUGIN_EXECUTABLE_NAME} 17 | CFBundlePackageType 18 | ${MACOSX_PLUGIN_BUNDLE_TYPE} 19 | CFBundleSupportedPlatforms 20 | 21 | MacOSX 22 | 23 | LSMinimumSystemVersion 24 | 10.13 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/obs-utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | extern gs_texrender_t *create_or_reset_texrender(gs_texrender_t *render); 12 | gs_texrender_t* create_or_reset_texrender_high(gs_texrender_t* render); 13 | extern void set_blending_parameters(); 14 | extern void label_indent(char *label, const char *label_text); 15 | extern void set_render_parameters(); 16 | extern bool add_source_to_list(void *data, obs_source_t *source); 17 | gs_effect_t *load_shader_effect(gs_effect_t *effect, 18 | const char *effect_file_path); 19 | extern char *load_shader_from_file(const char *file_name); 20 | extern void setting_visibility(const char *prop_name, bool visible, 21 | obs_properties_t *props); 22 | extern void texrender_set_texture(gs_texture_t *source, gs_texrender_t *dest); 23 | extern float (*move_get_transition_filter)(obs_source_t *filter_from, 24 | obs_source_t **filter_to); 25 | -------------------------------------------------------------------------------- /resource.rc.in: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 3 | PRODUCTVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 4 | FILEFLAGSMASK 0x0L 5 | #ifdef _DEBUG 6 | FILEFLAGS 0x1L 7 | #else 8 | FILEFLAGS 0x0L 9 | #endif 10 | FILEOS 0x0L 11 | FILETYPE 0x2L 12 | FILESUBTYPE 0x0L 13 | BEGIN 14 | BLOCK "StringFileInfo" 15 | BEGIN 16 | BLOCK "040904b0" 17 | BEGIN 18 | VALUE "CompanyName", "FiniteSingularity" 19 | VALUE "FileDescription", "${PROJECT_FULL_NAME}" 20 | VALUE "FileVersion", "${PROJECT_VERSION}" 21 | VALUE "InternalName", "${PROJECT_NAME}" 22 | VALUE "LegalCopyright", "(C) FiniteSingularity" 23 | VALUE "OriginalFilename", "${PROJECT_NAME}" 24 | VALUE "ProductName", "${PROJECT_FULL_NAME}" 25 | VALUE "ProductVersion", "${PROJECT_VERSION}" 26 | END 27 | END 28 | BLOCK "VarFileInfo" 29 | BEGIN 30 | VALUE "Translation", 0x409, 1200 31 | END 32 | END 33 | -------------------------------------------------------------------------------- /src/color-adjustments.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | struct color_adjustments_data; 10 | typedef struct color_adjustments_data color_adjustments_data_t; 11 | 12 | struct color_adjustments_data { 13 | // Adjustment Values 14 | bool adj_brightness; 15 | float brightness; 16 | float min_brightness; 17 | float max_brightness; 18 | 19 | bool adj_contrast; 20 | float min_contrast; 21 | float max_contrast; 22 | 23 | bool adj_saturation; 24 | float min_saturation; 25 | float max_saturation; 26 | 27 | bool adj_hue_shift; 28 | float min_hue_shift; 29 | float max_hue_shift; 30 | }; 31 | 32 | extern void color_adjustments_update(color_adjustments_data_t *data, 33 | obs_data_t *settings); 34 | extern void color_adjustments_properties(obs_properties_t *props); 35 | extern void color_adjustments_defaults(obs_data_t *settings); 36 | static bool setting_mask_adjustment_modified(obs_properties_t *props, 37 | obs_property_t *p, 38 | obs_data_t *settings); 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_linux: -------------------------------------------------------------------------------- 1 | autoload -Uz log_info log_status log_error log_debug log_warning 2 | 3 | log_debug 'Checking for apt-get...' 4 | if (( ! ${+commands[apt-get]} )) { 5 | log_error 'No apt-get command found. Please install apt' 6 | return 2 7 | } else { 8 | log_debug "Apt-get located at ${commands[apt-get]}" 9 | } 10 | 11 | local -a dependencies=("${(f)$(<${SCRIPT_HOME}/.Aptfile)}") 12 | local -a install_list 13 | local binary 14 | 15 | for dependency (${dependencies}) { 16 | local -a tokens=(${(s: :)dependency//(,|:|\')/}) 17 | 18 | if [[ ! ${tokens[1]} == 'package' ]] continue 19 | 20 | if [[ ${#tokens} -gt 2 && ${tokens[3]} == 'bin' ]] { 21 | binary=${tokens[4]} 22 | } else { 23 | binary=${tokens[2]} 24 | } 25 | 26 | if (( ! ${+commands[${binary}]} )) install_list+=(${tokens[2]}) 27 | } 28 | 29 | local -a _quiet=('' '--quiet') 30 | 31 | log_debug "List of dependencies to install: ${install_list}" 32 | if (( ${#install_list} )) { 33 | if (( ! ${+CI} )) log_warning 'Dependency installation via apt may require elevated privileges' 34 | 35 | sudo apt-get -y install ${install_list} ${_quiet[(( (_loglevel == 0) + 1 ))]} 36 | } 37 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Invoke-External.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-External { 2 | <# 3 | .SYNOPSIS 4 | Invokes a non-PowerShell command. 5 | .DESCRIPTION 6 | Runs a non-PowerShell command, and captures its return code. 7 | Throws an exception if the command returns non-zero. 8 | .EXAMPLE 9 | Invoke-External 7z x $MyArchive 10 | #> 11 | 12 | if ( $args.Count -eq 0 ) { 13 | throw 'Invoke-External called without arguments.' 14 | } 15 | 16 | if ( ! ( Test-Path function:Log-Information ) ) { 17 | . $PSScriptRoot/Logger.ps1 18 | } 19 | 20 | $Command = $args[0] 21 | $CommandArgs = @() 22 | 23 | if ( $args.Count -gt 1) { 24 | $CommandArgs = $args[1..($args.Count - 1)] 25 | } 26 | 27 | $_EAP = $ErrorActionPreference 28 | $ErrorActionPreference = "Continue" 29 | 30 | Log-Debug "Invoke-External: ${Command} ${CommandArgs}" 31 | 32 | & $command $commandArgs 33 | $Result = $LASTEXITCODE 34 | 35 | $ErrorActionPreference = $_EAP 36 | 37 | if ( $Result -ne 0 ) { 38 | throw "${Command} ${CommandArgs} exited with non-zero code ${Result}." 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /data/shaders/render_output.effect: -------------------------------------------------------------------------------- 1 | uniform float4x4 ViewProj; 2 | uniform texture2d image; 3 | uniform texture2d output_image; 4 | 5 | sampler_state textureSampler{ 6 | Filter = Linear; 7 | AddressU = Clamp; 8 | AddressV = Clamp; 9 | MinLOD = 0; 10 | MaxLOD = 0; 11 | }; 12 | 13 | struct VertData 14 | { 15 | float4 pos : POSITION; 16 | float2 uv : TEXCOORD0; 17 | }; 18 | 19 | VertData mainTransform(VertData v_in) 20 | { 21 | v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); 22 | return v_in; 23 | } 24 | 25 | float srgb_nonlinear_to_linear_channel(float u) 26 | { 27 | return (u <= 0.04045) ? (u / 12.92) : pow((u + 0.055) / 1.055, 2.4); 28 | } 29 | 30 | float3 srgb_nonlinear_to_linear(float3 v) 31 | { 32 | return float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b)); 33 | } 34 | 35 | float4 mainImage(VertData v_in) : TARGET 36 | { 37 | float4 px = output_image.Sample(textureSampler, v_in.uv); 38 | px.xyz = srgb_nonlinear_to_linear(px.xyz); 39 | return px; 40 | } 41 | 42 | technique Draw 43 | { 44 | pass 45 | { 46 | vertex_shader = mainTransform(v_in); 47 | pixel_shader = mainImage(v_in); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/obs-advanced-masks-plugin.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "version.h" 4 | 5 | extern struct obs_source_info advanced_masks_filter; 6 | extern struct obs_source_info advanced_masks_filter_v2; 7 | float (*move_get_transition_filter)(obs_source_t *filter_from, 8 | obs_source_t **filter_to) = NULL; 9 | 10 | OBS_DECLARE_MODULE(); 11 | 12 | OBS_MODULE_USE_DEFAULT_LOCALE("obs-advanced-masks", "en-US"); 13 | 14 | OBS_MODULE_AUTHOR("FiniteSingularity"); 15 | 16 | bool obs_module_load(void) 17 | { 18 | blog(LOG_INFO, "[Advanced Masks] loaded version %s", PROJECT_VERSION); 19 | obs_register_source(&advanced_masks_filter); 20 | obs_register_source(&advanced_masks_filter_v2); 21 | return true; 22 | } 23 | 24 | void obs_module_unload(void) {} 25 | 26 | void obs_module_post_load() 27 | { 28 | if (obs_get_module("move-transition") == NULL) 29 | return; 30 | proc_handler_t *ph = obs_get_proc_handler(); 31 | struct calldata cd; 32 | calldata_init(&cd); 33 | calldata_set_string(&cd, "filter_id", advanced_masks_filter.id); 34 | if (proc_handler_call(ph, "move_get_transition_filter_function", &cd)) { 35 | move_get_transition_filter = calldata_ptr(&cd, "callback"); 36 | } 37 | calldata_free(&cd); 38 | } 39 | -------------------------------------------------------------------------------- /data/shaders/feather-mask.effect: -------------------------------------------------------------------------------- 1 | uniform float4x4 ViewProj; 2 | uniform texture2d image; 3 | uniform float2 uv_size; 4 | uniform float feather_size; 5 | uniform texture2d distance_field; 6 | 7 | sampler_state textureSampler { 8 | Filter = Linear; 9 | AddressU = Clamp; 10 | AddressV = Clamp; 11 | }; 12 | 13 | sampler_state sdfSampler{ 14 | Filter = Point; 15 | AddressU = Clamp; 16 | AddressV = Clamp; 17 | MinLOD = 0; 18 | MaxLOD = 0; 19 | //BorderColor = 0xFFFFFFFF; 20 | }; 21 | 22 | 23 | struct VertData { 24 | float4 pos : POSITION; 25 | float2 uv : TEXCOORD0; 26 | }; 27 | 28 | VertData VSDefault(VertData v_in) 29 | { 30 | VertData vert_out; 31 | vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); 32 | vert_out.uv = v_in.uv; 33 | return vert_out; 34 | } 35 | 36 | float4 PSFeather(VertData v_in) : TARGET 37 | { 38 | float2 coord = v_in.uv * uv_size; 39 | float4 rgba = image.Sample(textureSampler, v_in.uv); 40 | float4 dist_texture = distance_field.Sample(sdfSampler, v_in.uv); 41 | float mask = saturate(distance(coord, dist_texture.xy) / feather_size); 42 | float4 retColor = float4(rgba.r, rgba.g, rgba.b, rgba.a * mask); 43 | return retColor; 44 | } 45 | 46 | technique Draw 47 | { 48 | pass 49 | { 50 | vertex_shader = VSDefault(v_in); 51 | pixel_shader = PSFeather(v_in); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.github/scripts/check-cmake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | if [ ${#} -eq 1 -a "${1}" = "VERBOSE" ]; then 7 | VERBOSITY="-l debug" 8 | else 9 | VERBOSITY="" 10 | fi 11 | 12 | if [ "${CI}" ]; then 13 | MODE="--check" 14 | else 15 | MODE="-i" 16 | fi 17 | 18 | # Runs the formatter in parallel on the code base. 19 | # Return codes: 20 | # - 1 there are files to be formatted 21 | # - 0 everything looks fine 22 | 23 | # Get CPU count 24 | OS=$(uname) 25 | NPROC=1 26 | if [[ ${OS} = "Linux" ]] ; then 27 | NPROC=$(nproc) 28 | elif [[ ${OS} = "Darwin" ]] ; then 29 | NPROC=$(sysctl -n hw.physicalcpu) 30 | fi 31 | 32 | # Discover clang-format 33 | if ! type cmake-format 2> /dev/null ; then 34 | echo "Required cmake-format not found" 35 | exit 1 36 | fi 37 | 38 | find . -type d \( \ 39 | -path ./\*build\* -o \ 40 | -path ./release -o \ 41 | -path ./deps/jansson -o \ 42 | -path ./plugins/decklink/\*/decklink-sdk -o \ 43 | -path ./plugins/enc-amf -o \ 44 | -path ./plugins/mac-syphon/syphon-framework -o \ 45 | -path ./plugins/obs-outputs/ftl-sdk -o \ 46 | -path ./plugins/obs-vst -o \ 47 | -path ./plugins/obs-browser -o \ 48 | -path ./plugins/win-dshow/libdshowcapture -o \ 49 | -path ./plugins/obs-websocket/deps \ 50 | \) -prune -false -type f -o \ 51 | -name 'CMakeLists.txt' -or \ 52 | -name '*.cmake' \ 53 | | xargs -L10 -P ${NPROC} cmake-format ${MODE} ${VERBOSITY} 54 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/read_codesign_pass: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Apple Developer credentials necessary: 3 | # 4 | # + Signing for distribution and notarization require an active Apple 5 | # Developer membership 6 | # + An Apple Development identity is needed for code signing 7 | # (i.e. 'Apple Development: YOUR APPLE ID (PROVIDER)') 8 | # + Your Apple developer ID is needed for notarization 9 | # + An app-specific password is necessary for notarization from CLI 10 | # + This password will be stored in your macOS keychain under the identifier 11 | # 'OBS-Codesign-Password'with access Apple's 'altool' only. 12 | ############################################################################## 13 | 14 | autoload -Uz read_codesign read_codesign_user log_info 15 | 16 | if (( ! ${+CODESIGN_IDENT} )) { 17 | read_codesign 18 | } 19 | 20 | local codesign_ident_short=$(print "${CODESIGN_IDENT}" | /usr/bin/sed -En 's/.+\((.+)\)/\1/p') 21 | 22 | if (( ! ${+CODESIGN_IDENT_USER} )) { 23 | read_codesign_user 24 | } 25 | 26 | log_info 'Setting up password for notarization keychain...' 27 | if (( ! ${+CODESIGN_IDENT_PASS} )) { 28 | read -s CODESIGN_IDENT_PASS'?Apple Developer ID password: ' 29 | } 30 | 31 | print '' 32 | log_info 'Setting up notarization keychain...' 33 | xcrun notarytool store-credentials 'OBS-Codesign-Password' --apple-id "${CODESIGN_IDENT_USER}" --team-id "${codesign_ident_short}" --password "${CODESIGN_IDENT_PASS}" 34 | -------------------------------------------------------------------------------- /src/mask-feather.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "base-filter.h" 5 | #include "color-adjustments.h" 6 | 7 | struct mask_feather_data; 8 | typedef struct mask_feather_data mask_feather_data_t; 9 | 10 | struct mask_feather_data { 11 | gs_effect_t* effect_feather_mask; 12 | gs_effect_t* effect_jump_flood_sdf; 13 | 14 | gs_texrender_t* buffer_a; 15 | gs_texrender_t* buffer_b; 16 | 17 | gs_eparam_t* param_feather_distance_field; 18 | gs_eparam_t* param_feather_image; 19 | gs_eparam_t* param_feather_size; 20 | gs_eparam_t* param_feather_uv_size; 21 | 22 | gs_eparam_t* param_jf_uv_size; 23 | gs_eparam_t* param_jf_offset; 24 | 25 | float featherSize; 26 | float intensity; 27 | }; 28 | 29 | extern mask_feather_data_t* mask_feather_create(); 30 | extern void mask_feather_destroy(mask_feather_data_t *data); 31 | extern void mask_feather_update(mask_feather_data_t *data, 32 | obs_data_t *settings); 33 | extern void feather_mask_properties(obs_properties_t *props); 34 | extern void mask_feather_defaults(obs_data_t *settings); 35 | extern void render_feather_mask(mask_feather_data_t *data, 36 | base_filter_data_t *base); 37 | static void load_feather_effect_files(mask_feather_data_t *data); 38 | static void load_feather_mask_effect(mask_feather_data_t *data); 39 | static void load_jump_flood_sdf_effect(mask_feather_data_t* data); 40 | static void render_jf_inner_threshold(mask_feather_data_t* data, base_filter_data_t* base); 41 | static void render_jf_passes_inner(mask_feather_data_t* data, base_filter_data_t* base, float maxExtent); 42 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/setup_linux: -------------------------------------------------------------------------------- 1 | autoload -Uz log_error log_status log_info mkcd 2 | 3 | if (( ! ${+project_root} )) { 4 | log_error "'project_root' not set. Please set before running ${0}." 5 | return 2 6 | } 7 | 8 | if (( ! ${+target} )) { 9 | log_error "'target' not set. Please set before running ${0}." 10 | return 2 11 | } 12 | 13 | pushd ${project_root} 14 | 15 | typeset -g QT_VERSION 16 | read -r QT_VERSION <<< \ 17 | "$(jq -r --arg target "${target}" \ 18 | '.platformConfig[$target] | { qtVersion } | join(" ")' \ 19 | ${project_root}/buildspec.json)" 20 | 21 | if (( ! (${skips[(Ie)all]} + ${skips[(Ie)deps]}) )) { 22 | log_info 'Installing obs build dependencies...' 23 | 24 | sudo apt-get install -y \ 25 | build-essential \ 26 | libcurl4-openssl-dev \ 27 | libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev \ 28 | libswresample-dev libswscale-dev \ 29 | libjansson-dev \ 30 | libx11-xcb-dev \ 31 | libgles2-mesa-dev \ 32 | libwayland-dev \ 33 | libpulse-dev 34 | 35 | local -a _qt_packages=() 36 | 37 | if (( QT_VERSION == 5 )) { 38 | _qt_packages+=( 39 | qtbase5-dev 40 | libqt5svg5-dev 41 | qtbase5-private-dev 42 | libqt5x11extras5-dev 43 | ) 44 | } elif (( QT_VERSION == 6 )) { 45 | _qt_packages+=( 46 | qt6-base-dev 47 | libqt6svg6-dev 48 | qt6-base-private-dev 49 | ) 50 | } else { 51 | log_error "Unsupported Qt version '${QT_VERSION}' specified." 52 | return 2 53 | } 54 | 55 | sudo apt-get install -y ${_qt_packages} 56 | } 57 | 58 | local deps_version 59 | read -r deps_version <<< \ 60 | "$(jq -r '.dependencies.prebuilt.version' ${buildspec_file})" 61 | 62 | typeset -g OBS_DEPS_VERSION=${deps_version} 63 | -------------------------------------------------------------------------------- /src/advanced-masks-filter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "version.h" 5 | #include "advanced-masks.h" 6 | #include "obs-utils.h" 7 | 8 | static const char *advanced_masks_name(void *unused); 9 | static void *advanced_masks_create(obs_data_t *settings, obs_source_t *source); 10 | static void advanced_masks_destroy(void *data); 11 | static uint32_t advanced_masks_width(void *data); 12 | static uint32_t advanced_masks_height(void *data); 13 | static void advanced_masks_update(void *data, obs_data_t *settings); 14 | static void advanced_masks_update_v2(void *data, obs_data_t *settings); 15 | static void advanced_masks_video_render(void *data, gs_effect_t *effect); 16 | static bool advanced_masks_multi_pass(advanced_masks_data_t* filter); 17 | static obs_properties_t *advanced_masks_properties(void *data); 18 | static void advanced_masks_video_tick(void *data, float seconds); 19 | static void advanced_masks_defaults(obs_data_t *settings); 20 | static void advanced_masks_defaults_v2(obs_data_t *settings); 21 | extern void get_input_source(base_filter_data_t* filter); 22 | static void draw_output(advanced_masks_data_t *filter); 23 | static void advanced_masks_render_filter(advanced_masks_data_t *filter); 24 | static void render_mask(advanced_masks_data_t *filter); 25 | 26 | static bool setting_mask_effect_modified(void *data, obs_properties_t *props, 27 | obs_property_t *p, 28 | obs_data_t *settings); 29 | 30 | static bool setting_mask_adjustment_modified(obs_properties_t *props, 31 | obs_property_t *p, 32 | obs_data_t *settings); 33 | 34 | static bool setting_mask_type_modified(void *data, obs_properties_t *props, 35 | obs_property_t *p, obs_data_t *settings); 36 | static void load_output_effect(advanced_masks_data_t *filter); 37 | -------------------------------------------------------------------------------- /src/mask-gradient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "base-filter.h" 5 | #include "color-adjustments.h" 6 | 7 | struct mask_gradient_data; 8 | typedef struct mask_gradient_data mask_gradient_data_t; 9 | 10 | struct mask_gradient_data { 11 | gs_effect_t *effect_gradient_mask; 12 | 13 | // Parameters for gradient mask 14 | float gradient_width; 15 | float gradient_position; 16 | float gradient_rotation; 17 | bool gradient_invert; 18 | bool gradient_debug; 19 | 20 | // Shader Parameters 21 | gs_eparam_t *param_gradient_image; 22 | gs_eparam_t *param_gradient_width; 23 | gs_eparam_t *param_gradient_position; 24 | gs_eparam_t *param_gradient_rotation; 25 | gs_eparam_t *param_gradient_uv_size; 26 | gs_eparam_t *param_gradient_invert; 27 | gs_eparam_t *param_gradient_min_brightness; 28 | gs_eparam_t *param_gradient_max_brightness; 29 | gs_eparam_t *param_gradient_min_contrast; 30 | gs_eparam_t *param_gradient_max_contrast; 31 | gs_eparam_t *param_gradient_min_saturation; 32 | gs_eparam_t *param_gradient_max_saturation; 33 | gs_eparam_t *param_gradient_min_hue_shift; 34 | gs_eparam_t *param_gradient_max_hue_shift; 35 | }; 36 | 37 | extern mask_gradient_data_t *mask_gradient_create(); 38 | extern void mask_gradient_destroy(mask_gradient_data_t *data); 39 | extern void mask_gradient_update(mask_gradient_data_t *data, 40 | obs_data_t *settings); 41 | extern void gradient_mask_properties(obs_properties_t *props); 42 | extern void mask_gradient_defaults(obs_data_t *settings); 43 | extern void render_gradient_mask(mask_gradient_data_t *data, 44 | base_filter_data_t *base, 45 | color_adjustments_data_t *color_adj); 46 | 47 | static void load_gradient_effect_files(mask_gradient_data_t *data); 48 | static void load_gradient_mask_effect(mask_gradient_data_t *data); 49 | -------------------------------------------------------------------------------- /data/shaders/common.effect: -------------------------------------------------------------------------------- 1 | 2 | #define PI180 0.0174533f 3 | #define SQRT3 0.5773503f 4 | #define LUM_R 0.299f 5 | #define LUM_G 0.587f 6 | #define LUM_B 0.114f 7 | #define SIN30 0.5f 8 | #define COS30 0.8660254f 9 | #define PI 3.141592654f 10 | #define PI2 6.283185307f 11 | #define mod(x, y) (x - y * floor(x / y)) 12 | 13 | // Convert pre-multiplied rgba values 14 | // to linear rgba. 15 | float4 pmrgba_to_rgba(float4 color) 16 | { 17 | return saturate(float4(color.rgb / color.a, color.a)); 18 | } 19 | 20 | float4 adjust_brightness(float4 color, float brightness) 21 | { 22 | return saturate(float4(color.rgb + brightness, color.a)); 23 | } 24 | 25 | float4 adjustments(float4 color, float b, float c, float s, float hue_shift) { 26 | // Calculate Contrast Value 27 | float ca = c < 0.0f ? 1.0f / (-c + 1.0f) : (c + 1.0f); 28 | 29 | // Calculate Saturation Values 30 | float s_r = (1.0f - s) * LUM_R; 31 | float s_g = (1.0f - s) * LUM_G; 32 | float s_b = (1.0f - s) * LUM_B; 33 | 34 | float3 col = float3( 35 | ca * (s_r + s) * color.r + ca * s_g * color.g + ca * s_b * color.b + b, 36 | ca * s_r * color.r + ca * (s_g + s) * color.g + ca * s_b * color.b + b, 37 | ca * s_r * color.r + ca * s_g * color.g + ca * (s_b + s) * color.b + b 38 | ); 39 | 40 | // Calculate Hue shift values 41 | float half_angle = 0.5f * hue_shift * PI180; 42 | float rq = SQRT3 * sin(half_angle); 43 | float cross = rq * rq; 44 | float sq = 2.0f * cross; 45 | float d = 2.0f * (0.5f - sq); 46 | float w_imag = rq * cos(half_angle); 47 | float a_l = 2.0f * (cross + w_imag); 48 | float b_l = 2.0f * (cross - w_imag); 49 | 50 | 51 | return saturate(float4( 52 | col.r * d + col.g * a_l + col.b * b_l, 53 | col.r * b_l + col.g * d + col.b * a_l, 54 | col.r * a_l + col.g * b_l + col.b * d, 55 | color.a 56 | )); 57 | } 58 | -------------------------------------------------------------------------------- /.github/scripts/check-format.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh 3 | 4 | set -o errexit 5 | set -o pipefail 6 | set -o nounset 7 | 8 | if [ ${#} -eq 1 ]; then 9 | VERBOSITY="--verbose" 10 | else 11 | VERBOSITY="" 12 | fi 13 | 14 | # Runs the Clang Formatter in parallel on the code base. 15 | # Return codes: 16 | # - 1 there are files to be formatted 17 | # - 0 everything looks fine 18 | 19 | # Get CPU count 20 | OS=$(uname) 21 | NPROC=1 22 | if [[ ${OS} = "Linux" ]] ; then 23 | NPROC=$(nproc) 24 | elif [[ ${OS} = "Darwin" ]] ; then 25 | NPROC=$(sysctl -n hw.physicalcpu) 26 | fi 27 | 28 | # Discover clang-format 29 | if type clang-format-13 2> /dev/null ; then 30 | CLANG_FORMAT=clang-format-13 31 | elif type clang-format 2> /dev/null ; then 32 | # Clang format found, but need to check version 33 | CLANG_FORMAT=clang-format 34 | V=$(clang-format --version) 35 | if [[ $V != *"version 13.0"* ]]; then 36 | echo "clang-format is not 13.0 (returned ${V})" 37 | exit 1 38 | fi 39 | else 40 | echo "No appropriate clang-format found (expected clang-format-13.0.0, or clang-format)" 41 | exit 1 42 | fi 43 | 44 | find . -type d \( \ 45 | -path ./\*build\* -o \ 46 | -path ./release -o \ 47 | -path ./cmake -o \ 48 | -path ./plugins/decklink/\*/decklink-sdk -o \ 49 | -path ./plugins/enc-amf -o \ 50 | -path ./plugins/mac-syphon/syphon-framework -o \ 51 | -path ./plugins/obs-outputs/ftl-sdk -o \ 52 | -path ./plugins/obs-websocket/deps \ 53 | \) -prune -false -type f -o \ 54 | -name '*.h' -or \ 55 | -name '*.hpp' -or \ 56 | -name '*.m' -or \ 57 | -name '*.mm' -or \ 58 | -name '*.c' -or \ 59 | -name '*.cpp' \ 60 | | xargs -L100 -P ${NPROC} "${CLANG_FORMAT}" ${VERBOSITY} -i -style=file -fallback-style=none 61 | -------------------------------------------------------------------------------- /data/shaders/super-key.effect: -------------------------------------------------------------------------------- 1 | uniform float4x4 ViewProj; 2 | 3 | uniform texture2d image; 4 | uniform float k; 5 | uniform float k2; 6 | uniform float veil; 7 | 8 | sampler_state textureSampler{ 9 | Filter = Linear; 10 | AddressU = Clamp; 11 | AddressV = Clamp; 12 | }; 13 | 14 | struct VertData 15 | { 16 | float4 pos : POSITION; 17 | float2 uv : TEXCOORD0; 18 | }; 19 | 20 | VertData mainTransform(VertData v_in) 21 | { 22 | v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); 23 | return v_in; 24 | } 25 | 26 | float4 mainImage(VertData v_in) : TARGET 27 | { 28 | float4 base = image.Sample(textureSampler, v_in.uv); 29 | 30 | float E_c = clamp(k * base.g - k2 * (base.b + base.r) / 2.0, 0.0, 1.0); 31 | float4 keyed = (1.0 - E_c) * base; 32 | 33 | float g = clamp(keyed.g, 0.0, 34 | 1.0 * keyed.b 35 | + 1.0 * max(keyed.b - keyed.r, 0.0) 36 | + 0.3 * max(keyed.r - keyed.b, 0.0) 37 | + 0.0 * min(keyed.b, keyed.r) 38 | ); 39 | return float4(keyed.r, g, keyed.b, 1.0 - E_c) - veil * E_c; 40 | } 41 | 42 | float4 mainImageMatte(VertData v_in) : TARGET 43 | { 44 | float4 base = image.Sample(textureSampler, v_in.uv); 45 | 46 | float E_c = clamp(k * base.g - k2 * (base.b + base.r) / 2.0, 0.0, 1.0); 47 | //float4 keyed = (1.0 - E_c) * base; 48 | 49 | //float g = clamp(keyed.g, 0.0, 50 | // 1.0 * keyed.b 51 | // + 1.0 * max(keyed.b - keyed.r, 0.0) 52 | // + 0.3 * max(keyed.r - keyed.b, 0.0) 53 | // + 0.0 * min(keyed.b, keyed.r) 54 | //); 55 | //return float4(keyed.r, g, keyed.b, 1.0 - E_c) - veil * E_c; 56 | float matte = 1.0 - (1.0 - E_c - veil * E_c); 57 | return float4(matte, matte, matte, 1.0); 58 | 59 | } 60 | 61 | technique Draw 62 | { 63 | pass 64 | { 65 | vertex_shader = mainTransform(v_in); 66 | pixel_shader = mainImage(v_in); 67 | } 68 | } 69 | 70 | technique DrawMatte 71 | { 72 | pass 73 | { 74 | vertex_shader = mainTransform(v_in); 75 | pixel_shader = mainImageMatte(v_in); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /.github/scripts/utils.zsh/check_packages: -------------------------------------------------------------------------------- 1 | if (( ! ${+commands[packagesbuild]} )) { 2 | autoload -Uz log_info log_status mkcd 3 | 4 | if (( ! ${+commands[curl]} )) { 5 | log_error 'curl not found. Please install curl.' 6 | return 2 7 | } 8 | 9 | if (( ! ${+project_root} )) { 10 | log_error "'project_root' not set. Please set before running ${0}." 11 | return 2 12 | } 13 | 14 | local -a curl_opts=() 15 | if (( ! ${+CI} )) { 16 | curl_opts+=(--progress-bar) 17 | } else { 18 | curl_opts+=(--show-error --silent) 19 | } 20 | curl_opts+=(--location ${@}) 21 | 22 | log_info 'Installing Packages.app...' 23 | 24 | pushd 25 | mkcd ${project_root:h}/obs-build-dependencies 26 | 27 | local packages_url='http://s.sudre.free.fr/Software/files/Packages_1210_1.dmg' 28 | # local packages_url='https://web.archive.org/web/20230727054218/http://s.sudre.free.fr/Software/files/Packages.dmg' 29 | local packages_hash='6afdd25386295974dad8f078b8f1e41cabebd08e72d970bf92f707c7e48b16c9' 30 | 31 | if [[ ! -f Packages.dmg ]] { 32 | log_status 'Download Packages.app' 33 | curl ${curl_opts} -o Packages.dmg ${packages_url} 34 | } 35 | 36 | local image_checksum 37 | read -r image_checksum _ <<< "$(sha256sum Packages.dmg)" 38 | 39 | if [[ ${packages_hash} != ${image_checksum} ]] { 40 | log_error "Checksum mismatch of Packages.app download. 41 | Expected : ${packages_hash} 42 | Actual : ${image_checksum}" 43 | return 2 44 | } 45 | 46 | hdiutil attach -noverify Packages.dmg &> /dev/null && log_status 'Packages.dmg image mounted.' 47 | 48 | log_info 'Installing Packages.app...' 49 | packages_volume=$(hdiutil info -plist | grep '/Volumes/Packages' | sed 's/.*\(\/Volumes\/[^<]*\)<\/string>/\1/') 50 | 51 | sudo installer -pkg "${packages_volume}/packages/Packages.pkg" -target / && rehash 52 | hdiutil detach ${packages_volume} &> /dev/null && log_status 'Packages.dmg image unmounted.' 53 | } 54 | -------------------------------------------------------------------------------- /data/shaders/jump-flood.effect: -------------------------------------------------------------------------------- 1 | uniform float4x4 ViewProj; 2 | uniform texture2d image; 3 | uniform float2 uv_size; 4 | uniform float offset; 5 | 6 | sampler_state sdfSampler{ 7 | Filter = Point; 8 | AddressU = Clamp; 9 | AddressV = Clamp; 10 | MinLOD = 0; 11 | MaxLOD = 0; 12 | //BorderColor = 0xFFFFFFFF; 13 | }; 14 | 15 | sampler_state textureSampler { 16 | Filter = Linear; 17 | AddressU = Clamp; 18 | AddressV = Clamp; 19 | }; 20 | 21 | struct VertData 22 | { 23 | float4 pos : POSITION; 24 | float2 uv : TEXCOORD0; 25 | }; 26 | 27 | VertData mainTransform(VertData v_in) 28 | { 29 | v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); 30 | return v_in; 31 | } 32 | 33 | float4 drawThresholdImageInner(VertData v_in) : TARGET 34 | { 35 | float2 coord = v_in.uv * uv_size; 36 | float4 c = image.Sample(textureSampler, v_in.uv); 37 | float a = c.a == 0.0 ? 1.0 : 0.0; 38 | //return float4(a, a, a, 1.0); 39 | return lerp(float4(32000.0, 32000.0, 0.0, 0.0), float4(coord, 0.0, 0.0), float4(a, a, a, a)); 40 | } 41 | 42 | float4 drawJumpFloodImage(VertData v_in) : TARGET 43 | { 44 | float2 coord = v_in.uv * uv_size; 45 | float minDist = 1.0e9; 46 | float4 nearest = float4(32000.0, 32000.0, 0.0, 0.0); 47 | 48 | for (int x = -1; x <= 1; x++) 49 | { 50 | for (int y = -1; y <= 1; y++) 51 | { 52 | float4 coord_s = image.Sample(sdfSampler, (coord + (offset * float2(x, y))) / uv_size); 53 | float2 d = coord - coord_s.xy; 54 | float d2 = dot(d, d); 55 | //float d2 = distance(coord, coord_s.xy); 56 | if (d2 < minDist) 57 | { 58 | minDist = d2; 59 | nearest = float4(coord_s.xy, 0.0, 0.0); 60 | } 61 | 62 | } 63 | 64 | } 65 | 66 | return nearest; 67 | } 68 | 69 | technique DrawThresholdInner 70 | { 71 | pass 72 | { 73 | vertex_shader = mainTransform(v_in); 74 | pixel_shader = drawThresholdImageInner(v_in); 75 | } 76 | } 77 | 78 | technique DrawJumpFloodStep 79 | { 80 | pass 81 | { 82 | vertex_shader = mainTransform(v_in); 83 | pixel_shader = drawJumpFloodImage(v_in); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/base-filter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PLUGIN_INFO \ 6 | "Advanced Masks (" PROJECT_VERSION \ 7 | ") by FiniteSingularity" 8 | 9 | #define MASK_EFFECT_ALPHA 1 10 | #define MASK_EFFECT_ALPHA_LABEL "AdvancedMasks.Effects.Alpha" 11 | #define MASK_EFFECT_ADJUSTMENT 2 12 | #define MASK_EFFECT_ADJUSTMENT_LABEL "AdvancedMasks.Effects.Adjustment" 13 | 14 | #define MASK_TYPE_SHAPE 1 15 | #define MASK_TYPE_SHAPE_LABEL "AdvancedMasks.Shape" 16 | #define MASK_TYPE_SOURCE 2 17 | #define MASK_TYPE_SOURCE_LABEL "AdvancedMasks.Source" 18 | #define MASK_TYPE_IMAGE 3 19 | #define MASK_TYPE_IMAGE_LABEL "AdvancedMasks.Image" 20 | #define MASK_TYPE_GRADIENT 4 21 | #define MASK_TYPE_GRADIENT_LABEL "AdvancedMasks.Gradient" 22 | #define MASK_TYPE_BSM 5 23 | #define MASK_TYPE_BSM_LABEL "AdvancedMasks.BSM" 24 | #define MASK_TYPE_CHROMA_KEY 6 25 | #define MASK_TYPE_CHROMA_KEY_LABEL "AdvancedMasks.ChromaKey" 26 | #define MASK_TYPE_FEATHER 7 27 | #define MASK_TYPE_FEATHER_LABEL "AdvancedMasks.FeatherMask" 28 | #define MASK_TYPE_SVG 8 29 | #define MASK_TYPE_SVG_LABEL "AdvancedMasks.SvgMask" 30 | #define MASK_TYPE_FONT_AWESOME 9 31 | #define MASK_TYPE_FONT_AWESOME_LABEL "AdvancedMasks.FontAwesome" 32 | 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | struct base_filter_data; 38 | typedef struct base_filter_data base_filter_data_t; 39 | 40 | struct base_filter_data { 41 | obs_source_t* context; 42 | 43 | bool input_texture_generated; 44 | gs_texrender_t* input_texrender; 45 | bool output_rendered; 46 | gs_texrender_t* output_texrender; 47 | gs_effect_t* output_effect; 48 | gs_eparam_t* param_output_image; 49 | 50 | bool rendered; 51 | bool rendering; 52 | 53 | uint32_t width; 54 | uint32_t height; 55 | 56 | uint32_t mask_effect; 57 | uint32_t mask_type; 58 | }; 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Install-BuildDependencies.ps1: -------------------------------------------------------------------------------- 1 | function Install-BuildDependencies { 2 | <# 3 | .SYNOPSIS 4 | Installs required build dependencies. 5 | .DESCRIPTION 6 | Additional packages might be needed for successful builds. This module contains additional 7 | dependencies available for installation via winget and, if possible, adds their locations 8 | to the environment path for future invocation. 9 | .EXAMPLE 10 | Install-BuildDependencies 11 | #> 12 | 13 | param( 14 | [string] $WingetFile = "$PSScriptRoot/.Wingetfile" 15 | ) 16 | 17 | if ( ! ( Test-Path function:Log-Warning ) ) { 18 | . $PSScriptRoot/Logger.ps1 19 | } 20 | 21 | $Host64Bit = [System.Environment]::Is64BitOperatingSystem 22 | 23 | $Paths = $Env:Path -split [System.IO.Path]::PathSeparator 24 | 25 | $WingetOptions = @('install', '--accept-package-agreements', '--accept-source-agreements') 26 | 27 | if ( $script:Quiet ) { 28 | $WingetOptions += '--silent' 29 | } 30 | 31 | Get-Content $WingetFile | ForEach-Object { 32 | $_, $Package, $_, $Path, $_, $Binary = ([regex]::Split($_, " (?=(?:[^']|'[^']*')*$)")) -replace ',', '' -replace "'",'' 33 | 34 | (${Env:ProgramFiles(x86)}, $Env:ProgramFiles) | ForEach-Object { 35 | $Prefix = $_ 36 | $FullPath = "${Prefix}\${Path}" 37 | if ( ( Test-Path $FullPath ) -and ! ( $Paths -contains $FullPath ) ) { 38 | $Paths += $FullPath 39 | $Env:Path = $Paths -join [System.IO.Path]::PathSeparator 40 | } 41 | } 42 | 43 | Log-Debug "Checking for command ${Binary}" 44 | $Found = Get-Command -ErrorAction SilentlyContinue $Binary 45 | 46 | if ( $Found ) { 47 | Log-Status "Found dependency ${Binary} as $($Found.Source)" 48 | } else { 49 | Log-Status "Installing package ${Package}" 50 | 51 | try { 52 | $Params = $WingetOptions + $Package 53 | 54 | winget @Params 55 | } catch { 56 | throw "Error while installing winget package ${Package}: $_" 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.github/actions/build-plugin/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup and build plugin' 2 | description: 'Builds the plugin for specified architecture and build config.' 3 | inputs: 4 | target: 5 | description: 'Build target for dependencies' 6 | required: true 7 | config: 8 | description: 'Build configuration' 9 | required: false 10 | default: 'Release' 11 | codesign: 12 | description: 'Enable codesigning (macOS only)' 13 | required: false 14 | default: 'false' 15 | codesignIdent: 16 | description: 'Developer ID for application codesigning (macOS only)' 17 | required: false 18 | default: '-' 19 | visualStudio: 20 | description: 'Visual Studio version (Windows only)' 21 | required: false 22 | default: 'Visual Studio 16 2019' 23 | workingDirectory: 24 | description: 'Working directory for packaging' 25 | required: false 26 | default: ${{ github.workspace }} 27 | runs: 28 | using: 'composite' 29 | steps: 30 | - name: Run macOS Build 31 | if: ${{ runner.os == 'macOS' }} 32 | shell: zsh {0} 33 | env: 34 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 35 | run: | 36 | build_args=( 37 | -c ${{ inputs.config }} 38 | -t macos-${{ inputs.target }} 39 | ) 40 | 41 | if [[ '${{ inputs.codesign }}' == 'true' ]] build_args+=(-s) 42 | if (( ${+CI} && ${+RUNNER_DEBUG} )) build_args+=(--debug) 43 | 44 | ${{ inputs.workingDirectory }}/.github/scripts/build-macos.zsh ${build_args} 45 | 46 | - name: Run Linux Build 47 | if: ${{ runner.os == 'Linux' }} 48 | shell: bash 49 | run: | 50 | build_args=( 51 | -c ${{ inputs.config }} 52 | -t linux-${{ inputs.target }} 53 | ) 54 | 55 | if [[ -n "${CI}" && -n "${RUNNER_DEBUG}" ]]; then 56 | build_args+=(--debug) 57 | fi 58 | 59 | ${{ inputs.workingDirectory }}/.github/scripts/build-linux.sh "${build_args[@]}" 60 | 61 | - name: Run Windows Build 62 | if: ${{ runner.os == 'Windows' }} 63 | shell: pwsh 64 | run: | 65 | $BuildArgs = @{ 66 | Target = '${{ inputs.target }}' 67 | Configuration = '${{ inputs.config }}' 68 | CMakeGenerator = '${{ inputs.visualStudio }}' 69 | } 70 | 71 | if ( ( Test-Path env:CI ) -and ( Test-Path env:RUNNER_DEBUG ) ) { 72 | $BuildArgs += @{ 73 | Debug = $true 74 | } 75 | } 76 | 77 | ${{ inputs.workingDirectory }}/.github/scripts/Build-Windows.ps1 @BuildArgs 78 | -------------------------------------------------------------------------------- /src/mask-bsm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "base-filter.h" 6 | #include "color-adjustments.h" 7 | 8 | struct mask_bsm_data; 9 | typedef struct mask_bsm_data mask_bsm_data_t; 10 | 11 | struct mask_bsm_data { 12 | gs_texrender_t *bsm_mask_texrender; 13 | gs_texrender_t *bsm_buffer_texrender; 14 | gs_effect_t *effect_bsm_mask; 15 | 16 | obs_weak_source_t *mask_source_source; 17 | float fade_time; 18 | float seconds; 19 | float alpha_reduction; 20 | bool freeze_frame; 21 | 22 | // shader params 23 | gs_eparam_t *param_bsm_image; 24 | gs_eparam_t *param_bsm_buffer; 25 | gs_eparam_t *param_bsm_current_input_mask; 26 | gs_eparam_t *param_bsm_adjustment_mask; 27 | gs_eparam_t *param_bsm_alpha_reduction; 28 | gs_eparam_t *param_bsm_min_brightness; 29 | gs_eparam_t *param_bsm_max_brightness; 30 | gs_eparam_t *param_bsm_min_contrast; 31 | gs_eparam_t *param_bsm_max_contrast; 32 | gs_eparam_t *param_bsm_min_saturation; 33 | gs_eparam_t *param_bsm_max_saturation; 34 | gs_eparam_t *param_bsm_min_hue_shift; 35 | gs_eparam_t *param_bsm_max_hue_shift; 36 | }; 37 | 38 | extern mask_bsm_data_t *mask_bsm_create(); 39 | extern void mask_bsm_destroy(mask_bsm_data_t *data); 40 | extern void mask_bsm_update(mask_bsm_data_t *data, obs_data_t *settings); 41 | extern void mask_bsm_defaults(obs_data_t *settings); 42 | extern void bsm_mask_tick(mask_bsm_data_t *data, float seconds); 43 | extern void bsm_mask_top_properties(obs_properties_t *props); 44 | extern void bsm_mask_bot_properties(obs_properties_t *props); 45 | static void setup_adjustment_params(mask_bsm_data_t *data, color_adjustments_data_t *color_adj); 46 | static gs_texrender_t *get_mask_source_render(mask_bsm_data_t *data, base_filter_data_t *base); 47 | static void setup_bsm_params(mask_bsm_data_t *data, gs_texture_t *image_texture, 48 | gs_texture_t *cur_mask_texture, 49 | gs_texture_t *buffer_texture, 50 | bool reduce_alpha); 51 | extern void render_bsm_mask(mask_bsm_data_t *data, 52 | base_filter_data_t *base, 53 | color_adjustments_data_t *color_adj); 54 | static void render_bsm_alpha_mask(mask_bsm_data_t *data, base_filter_data_t *base); 55 | static void render_bsm_adjustment_mask(mask_bsm_data_t *data, 56 | base_filter_data_t *base, 57 | color_adjustments_data_t *color_adj); 58 | 59 | extern bool setting_mask_source_filter_modified(obs_properties_t *props, 60 | obs_property_t *p, 61 | obs_data_t *settings); 62 | 63 | static void load_bsm_effect_files(mask_bsm_data_t *data); 64 | static void load_bsm_mask_effect(mask_bsm_data_t *data); 65 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Expand-ArchiveExt.ps1: -------------------------------------------------------------------------------- 1 | function Expand-ArchiveExt { 2 | <# 3 | .SYNOPSIS 4 | Expands archive files. 5 | .DESCRIPTION 6 | Allows extraction of zip, 7z, gz, and xz archives. 7 | Requires tar and 7-zip to be available on the system. 8 | Archives ending with .zip but created using LZMA compression are 9 | expanded using 7-zip as a fallback. 10 | .EXAMPLE 11 | Expand-ArchiveExt -Path 12 | Expand-ArchiveExt -Path -DestinationPath 13 | #> 14 | 15 | param( 16 | [Parameter(Mandatory)] 17 | [string] $Path, 18 | [string] $DestinationPath = [System.IO.Path]::GetFileNameWithoutExtension($Path), 19 | [switch] $Force 20 | ) 21 | 22 | switch ( [System.IO.Path]::GetExtension($Path) ) { 23 | .zip { 24 | try { 25 | Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force:$Force 26 | } catch { 27 | if ( Get-Command 7z ) { 28 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 29 | } else { 30 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 31 | } 32 | } 33 | break 34 | } 35 | { ( $_ -eq ".7z" ) -or ( $_ -eq ".exe" ) } { 36 | if ( Get-Command 7z ) { 37 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 38 | } else { 39 | throw "Extraction utility 7-zip not found. Please install 7-zip first." 40 | } 41 | break 42 | } 43 | .gz { 44 | try { 45 | Invoke-External tar -x -o $DestinationPath -f $Path 46 | } catch { 47 | if ( Get-Command 7z ) { 48 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 49 | } else { 50 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 51 | } 52 | } 53 | break 54 | } 55 | .xz { 56 | try { 57 | Invoke-External tar -x -o $DestinationPath -f $Path 58 | } catch { 59 | if ( Get-Command 7z ) { 60 | Invoke-External 7z x -y $Path "-o${DestinationPath}" 61 | } else { 62 | throw "Fallback utility 7-zip not found. Please install 7-zip first." 63 | } 64 | } 65 | } 66 | default { 67 | throw "Unsupported archive extension provided." 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/mask-chroma-key.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "base-filter.h" 5 | 6 | #define KEY_ADVANCED 1 7 | #define KEY_ADVANCED_LABEL "AdvancedMasks.ChromaKey.Advanced" 8 | #define KEY_SUPER 2 9 | #define KEY_SUPER_LABEL "AdvancedMasks.ChromaKey.Super" 10 | 11 | #define KEY_COLOR_TYPE_SINGLE 1 12 | #define KEY_COLOR_TYPE_SINGLE_LABEL "AdvancedMasks.ChromaKey.Advanced.SingleColor" 13 | #define KEY_COLOR_TYPE_DOUBLE 2 14 | #define KEY_COLOR_TYPE_DOUBLE_LABEL "AdvancedMasks.ChromaKey.Advanced.DoubleColor" 15 | 16 | struct mask_chroma_key_data; 17 | typedef struct mask_chroma_key_data mask_chroma_key_data_t; 18 | 19 | struct mask_chroma_key_data { 20 | gs_effect_t* effect_super_key_mask; 21 | gs_effect_t* effect_advanced_key_mask; 22 | 23 | gs_eparam_t* param_super_key_image; 24 | gs_eparam_t* param_super_key_k; 25 | gs_eparam_t* param_super_key_k2; 26 | gs_eparam_t* param_super_key_veil; 27 | 28 | gs_eparam_t* param_advanced_key_image; 29 | gs_eparam_t* param_advanced_key_opacity; 30 | gs_eparam_t* param_advanced_key_contrast; 31 | gs_eparam_t* param_advanced_key_brightness; 32 | gs_eparam_t* param_advanced_key_gamma; 33 | gs_eparam_t* param_advanced_key_chroma_key; 34 | gs_eparam_t* param_advanced_key_pixel_size; 35 | gs_eparam_t* param_advanced_key_similarity; 36 | gs_eparam_t* param_advanced_key_smoothness; 37 | gs_eparam_t* param_advanced_key_spill; 38 | 39 | uint32_t keyType; 40 | uint32_t advancedColorType; 41 | bool showMatte; 42 | 43 | float k; 44 | float k2; 45 | float veil; 46 | 47 | float opacity; 48 | float brightness; 49 | float contrast; 50 | float gamma; 51 | float similarity; 52 | float spill; 53 | float smoothness; 54 | 55 | struct vec2 chroma; 56 | 57 | }; 58 | 59 | mask_chroma_key_data_t* mask_chroma_key_create(); 60 | void mask_chroma_key_destroy(mask_chroma_key_data_t* data); 61 | void mask_chroma_key_update(mask_chroma_key_data_t* data, 62 | obs_data_t* settings); 63 | void mask_chroma_key_defaults(obs_data_t* settings); 64 | void mask_chroma_key_properties(obs_properties_t* props); 65 | static void load_chroma_key_effect_files(mask_chroma_key_data_t* data); 66 | static void load_super_key_mask_effect(mask_chroma_key_data_t* data); 67 | static void load_advanced_key_mask_effect(mask_chroma_key_data_t* data); 68 | void render_chroma_key_mask(mask_chroma_key_data_t* data, base_filter_data_t* base); 69 | void render_advanced_key_mask(mask_chroma_key_data_t* data, 70 | base_filter_data_t* base); 71 | void render_super_key_mask(mask_chroma_key_data_t* data, 72 | base_filter_data_t* base); 73 | extern bool key_type_modified(obs_properties_t* props, 74 | obs_property_t* p, 75 | obs_data_t* settings); 76 | extern bool color_type_modified(obs_properties_t* props, 77 | obs_property_t* p, 78 | obs_data_t* settings); 79 | -------------------------------------------------------------------------------- /src/svg-utils.cpp: -------------------------------------------------------------------------------- 1 | #include "svg-utils.hpp" 2 | extern "C" { 3 | #include "mask-svg.h" 4 | #include "utils.h" 5 | } 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | gs_texture_t* gs_texture_from_svg_path(const char* path, int width, int height, int scale_by) 16 | { 17 | //std::string svgPath(path); 18 | int large = 1000000; 19 | QSvgRenderer svgRenderer(QString(path), nullptr); 20 | if (!svgRenderer.isValid()) { 21 | return nullptr; 22 | } 23 | QSize defSize = svgRenderer.defaultSize(); 24 | 25 | QSize imageSize; 26 | if (scale_by == 1) { 27 | imageSize = defSize.scaled(QSize(width, large), Qt::KeepAspectRatio); 28 | } else if (scale_by == 2) { 29 | imageSize = defSize.scaled(QSize(large, height), Qt::KeepAspectRatio); 30 | } else { 31 | imageSize.setWidth(width); 32 | imageSize.setHeight(height); 33 | } 34 | 35 | QImage image(imageSize, QImage::Format_RGBA8888); 36 | image.fill(Qt::transparent); 37 | 38 | QPainter painter(&image); 39 | svgRenderer.render(&painter); 40 | painter.end(); 41 | 42 | int bytesPerLine = image.bytesPerLine(); 43 | uint32_t cy = image.height(); 44 | uint32_t cx = image.width(); 45 | 46 | const uint8_t* ptr = image.constBits(); 47 | 48 | std::vector rawData(ptr, ptr + (bytesPerLine * cy)); 49 | 50 | enum gs_color_format format = GS_RGBA; 51 | obs_enter_graphics(); 52 | gs_texture_t* tex = gs_texture_create(cx, cy, format, 1, (const uint8_t**)&ptr, 0); 53 | obs_leave_graphics(); 54 | return tex; 55 | } 56 | 57 | gs_texture_t* gs_texture_from_svg(const char* svg, int width, int height, int scale_by) 58 | { 59 | //std::string svgPath(path); 60 | int large = 1000000; 61 | QSvgRenderer svgRenderer(QString(svg).toUtf8()); 62 | if (!svgRenderer.isValid()) { 63 | return nullptr; 64 | } 65 | QSize defSize = svgRenderer.defaultSize(); 66 | 67 | QSize imageSize; 68 | if (scale_by == 1) { 69 | imageSize = defSize.scaled(QSize(width, large), Qt::KeepAspectRatio); 70 | } 71 | else if (scale_by == 2) { 72 | imageSize = defSize.scaled(QSize(large, height), Qt::KeepAspectRatio); 73 | } 74 | else { 75 | imageSize.setWidth(width); 76 | imageSize.setHeight(height); 77 | } 78 | 79 | QImage image(imageSize, QImage::Format_RGBA8888); 80 | image.fill(Qt::transparent); 81 | 82 | QPainter painter(&image); 83 | svgRenderer.render(&painter); 84 | painter.end(); 85 | 86 | int bytesPerLine = image.bytesPerLine(); 87 | uint32_t cy = image.height(); 88 | uint32_t cx = image.width(); 89 | 90 | const uint8_t* ptr = image.constBits(); 91 | 92 | std::vector rawData(ptr, ptr + (bytesPerLine * cy)); 93 | 94 | enum gs_color_format format = GS_RGBA; 95 | obs_enter_graphics(); 96 | gs_texture_t* tex = gs_texture_create(cx, cy, format, 1, (const uint8_t**)&ptr, 0); 97 | obs_leave_graphics(); 98 | return tex; 99 | } 100 | -------------------------------------------------------------------------------- /.github/scripts/Package-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 4 | [string] $Configuration = 'RelWithDebInfo', 5 | [ValidateSet('x86', 'x64', 'x86+x64')] 6 | [string] $Target, 7 | [switch] $BuildInstaller = $false 8 | ) 9 | 10 | $ErrorActionPreference = 'Stop' 11 | 12 | if ( $DebugPreference -eq 'Continue' ) { 13 | $VerbosePreference = 'Continue' 14 | $InformationPreference = 'Continue' 15 | } 16 | 17 | if ( $PSVersionTable.PSVersion -lt '7.0.0' ) { 18 | Write-Warning 'The obs-deps PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 19 | exit 2 20 | } 21 | 22 | function Package { 23 | trap { 24 | Write-Error $_ 25 | exit 2 26 | } 27 | 28 | $ScriptHome = $PSScriptRoot 29 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 30 | $BuildSpecFile = "${ProjectRoot}/buildspec.json" 31 | 32 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 33 | 34 | foreach( $Utility in $UtilityFunctions ) { 35 | Write-Debug "Loading $($Utility.FullName)" 36 | . $Utility.FullName 37 | } 38 | 39 | $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json 40 | $ProductName = $BuildSpec.name 41 | $ProductVersion = $BuildSpec.version 42 | 43 | $OutputName = "${ProductName}-${ProductVersion}-windows-${Target}" 44 | 45 | Install-BuildDependencies -WingetFile "${ScriptHome}/.Wingetfile" 46 | 47 | Log-Information "Packaging ${ProductName}..." 48 | 49 | $RemoveArgs = @{ 50 | ErrorAction = 'SilentlyContinue' 51 | Path = @( 52 | "${ProjectRoot}/release/${ProductName}-*-windows-*.zip" 53 | "${ProjectRoot}/release/${ProductName}-*-windows-*.exe" 54 | ) 55 | } 56 | 57 | Remove-Item @RemoveArgs 58 | 59 | if ( ( $BuildInstaller ) ) { 60 | if ( $Target -eq 'x86+x64' ) { 61 | $IsccCandidates = Get-ChildItem -Recurse -Path '*.iss' 62 | 63 | if ( $IsccCandidates.length -gt 0 ) { 64 | $IsccFile = $IsccCandidates[0].FullName 65 | } else { 66 | $IsccFile = '' 67 | } 68 | } else { 69 | $IsccFile = "${ProjectRoot}/build_${Target}/installer-Windows.generated.iss" 70 | } 71 | 72 | if ( ! ( Test-Path -Path $IsccFile ) ) { 73 | throw 'InnoSetup install script not found. Run the build script or the CMake build and install procedures first.' 74 | } 75 | 76 | Log-Information 'Creating InnoSetup installer...' 77 | Push-Location -Stack BuildTemp 78 | Ensure-Location -Path "${ProjectRoot}/release" 79 | Invoke-External iscc ${IsccFile} /O. /F"${OutputName}-Installer" 80 | Pop-Location -Stack BuildTemp 81 | } 82 | 83 | $CompressArgs = @{ 84 | Path = (Get-ChildItem -Path "${ProjectRoot}/release" -Exclude "${OutputName}*.*") 85 | CompressionLevel = 'Optimal' 86 | DestinationPath = "${ProjectRoot}/release/${OutputName}.zip" 87 | } 88 | 89 | Compress-Archive -Force @CompressArgs 90 | } 91 | 92 | Package 93 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Logger.ps1: -------------------------------------------------------------------------------- 1 | function Log-Debug { 2 | [CmdletBinding()] 3 | param( 4 | [Parameter(Mandatory,ValueFromPipeline)] 5 | [ValidateNotNullOrEmpty()] 6 | [string[]] $Message 7 | ) 8 | 9 | Process { 10 | foreach($m in $Message) { 11 | Write-Debug $m 12 | } 13 | } 14 | } 15 | 16 | function Log-Verbose { 17 | [CmdletBinding()] 18 | param( 19 | [Parameter(Mandatory,ValueFromPipeline)] 20 | [ValidateNotNullOrEmpty()] 21 | [string[]] $Message 22 | ) 23 | 24 | Process { 25 | foreach($m in $Message) { 26 | Write-Verbose $m 27 | } 28 | } 29 | } 30 | 31 | function Log-Warning { 32 | [CmdletBinding()] 33 | param( 34 | [Parameter(Mandatory,ValueFromPipeline)] 35 | [ValidateNotNullOrEmpty()] 36 | [string[]] $Message 37 | ) 38 | 39 | Process { 40 | foreach($m in $Message) { 41 | Write-Warning $m 42 | } 43 | } 44 | } 45 | 46 | function Log-Error { 47 | [CmdletBinding()] 48 | param( 49 | [Parameter(Mandatory,ValueFromPipeline)] 50 | [ValidateNotNullOrEmpty()] 51 | [string[]] $Message 52 | ) 53 | 54 | Process { 55 | foreach($m in $Message) { 56 | Write-Error $m 57 | } 58 | } 59 | } 60 | 61 | function Log-Information { 62 | [CmdletBinding()] 63 | param( 64 | [Parameter(Mandatory,ValueFromPipeline)] 65 | [ValidateNotNullOrEmpty()] 66 | [string[]] $Message 67 | ) 68 | 69 | Process { 70 | if ( ! ( $script:Quiet ) ) { 71 | $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) 72 | $Icon = ' =>' 73 | 74 | foreach($m in $Message) { 75 | Write-Host -NoNewLine -ForegroundColor Blue " ${StageName} $($Icon.PadRight(5)) " 76 | Write-Host "${m}" 77 | } 78 | } 79 | } 80 | } 81 | 82 | function Log-Status { 83 | [CmdletBinding()] 84 | param( 85 | [Parameter(Mandatory,ValueFromPipeline)] 86 | [ValidateNotNullOrEmpty()] 87 | [string[]] $Message 88 | ) 89 | 90 | Process { 91 | if ( ! ( $script:Quiet ) ) { 92 | $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' }) 93 | $Icon = ' >' 94 | 95 | foreach($m in $Message) { 96 | Write-Host -NoNewLine -ForegroundColor Green " ${StageName} $($Icon.PadRight(5)) " 97 | Write-Host "${m}" 98 | } 99 | } 100 | } 101 | } 102 | 103 | function Log-Output { 104 | [CmdletBinding()] 105 | param( 106 | [Parameter(Mandatory,ValueFromPipeline)] 107 | [ValidateNotNullOrEmpty()] 108 | [string[]] $Message 109 | ) 110 | 111 | Process { 112 | if ( ! ( $script:Quiet ) ) { 113 | $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' }) 114 | $Icon = '' 115 | 116 | foreach($m in $Message) { 117 | Write-Output " ${StageName} $($Icon.PadRight(5)) ${m}" 118 | } 119 | } 120 | } 121 | } 122 | 123 | $Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5 124 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Setup-Obs.ps1: -------------------------------------------------------------------------------- 1 | function Setup-Obs { 2 | if ( ! ( Test-Path function:Log-Output ) ) { 3 | . $PSScriptRoot/Logger.ps1 4 | } 5 | 6 | if ( ! ( Test-Path function:Check-Git ) ) { 7 | . $PSScriptRoot/Check-Git.ps1 8 | } 9 | 10 | Check-Git 11 | 12 | if ( ! ( Test-Path function:Ensure-Location ) ) { 13 | . $PSScriptRoot/Ensure-Location.ps1 14 | } 15 | 16 | if ( ! ( Test-Path function:Invoke-GitCheckout ) ) { 17 | . $PSScriptRoot/Invoke-GitCheckout.ps1 18 | } 19 | 20 | if ( ! ( Test-Path function:Invoke-External ) ) { 21 | . $PSScriptRoot/Invoke-External.ps1 22 | } 23 | 24 | Log-Information 'Setting up OBS Studio...' 25 | 26 | $ObsVersion = $BuildSpec.dependencies.'obs-studio'.version 27 | $ObsRepository = $BuildSpec.dependencies.'obs-studio'.repository 28 | $ObsBranch = $BuildSpec.dependencies.'obs-studio'.branch 29 | $ObsHash = $BuildSpec.dependencies.'obs-studio'.hash 30 | 31 | if ( $ObsVersion -eq '' ) { 32 | throw 'No obs-studio version found in buildspec.json.' 33 | } 34 | 35 | Push-Location -Stack BuildTemp 36 | Ensure-Location -Path "$(Resolve-Path -Path "${ProjectRoot}/../")/obs-studio" 37 | 38 | if ( ! ( ( $script:SkipAll ) -or ( $script:SkipUnpack ) ) ) { 39 | Invoke-GitCheckout -Uri $ObsRepository -Commit $ObsHash -Path . -Branch $ObsBranch 40 | } 41 | 42 | if ( ! ( ( $script:SkipAll ) -or ( $script:SkipBuild ) ) ) { 43 | Log-Information 'Configuring OBS Studio...' 44 | 45 | $NumProcessors = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors 46 | 47 | if ( $NumProcessors -gt 1 ) { 48 | $env:UseMultiToolTask = $true 49 | $env:EnforceProcessCountAcrossBuilds = $true 50 | } 51 | 52 | $DepsPath = "plugin-deps-${script:DepsVersion}-qt${script:QtVersion}-${script:Target}" 53 | 54 | $CmakeArgs = @( 55 | '-G', $CmakeGenerator 56 | "-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}" 57 | "-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })" 58 | "-DCMAKE_BUILD_TYPE=${script:Configuration}" 59 | "-DQT_VERSION=${script:QtVersion}" 60 | '-DENABLE_PLUGINS=OFF' 61 | '-DENABLE_UI=OFF' 62 | '-DENABLE_SCRIPTING=OFF' 63 | "-DCMAKE_INSTALL_PREFIX:PATH=$(Resolve-Path -Path "${ProjectRoot}/../obs-build-dependencies/${DepsPath}")" 64 | "-DCMAKE_PREFIX_PATH:PATH=$(Resolve-Path -Path "${ProjectRoot}/../obs-build-dependencies/${DepsPath}")" 65 | ) 66 | 67 | Log-Debug "Attempting to configure OBS with CMake arguments: $($CmakeArgs | Out-String)" 68 | Log-Information "Configuring OBS..." 69 | Invoke-External cmake -S . -B plugin_build_${script:Target} @CmakeArgs 70 | 71 | Log-Information 'Building libobs and obs-frontend-api...' 72 | $CmakeArgs = @( 73 | '--config', "$( if ( $script:Configuration -eq '' ) { 'RelWithDebInfo' } else { $script:Configuration })" 74 | ) 75 | 76 | if ( $VerbosePreference -eq 'Continue' ) { 77 | $CmakeArgs+=('--verbose') 78 | } 79 | 80 | Invoke-External cmake --build plugin_build_${script:Target} @CmakeArgs -t obs-frontend-api 81 | Invoke-External cmake --install plugin_build_${script:Target} @CmakeArgs --component obs_libraries 82 | } 83 | Pop-Location -Stack BuildTemp 84 | } 85 | -------------------------------------------------------------------------------- /.github/actions/package-plugin/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Package plugin' 2 | description: 'Packages the plugin for specified architecture and build config.' 3 | inputs: 4 | target: 5 | description: 'Build target for dependencies' 6 | required: true 7 | config: 8 | description: 'Build configuration' 9 | required: false 10 | default: 'Release' 11 | codesign: 12 | description: 'Enable codesigning (macOS only)' 13 | required: false 14 | default: 'false' 15 | notarize: 16 | description: 'Enable notarization (macOS only)' 17 | required: false 18 | default: 'false' 19 | codesignIdent: 20 | description: 'Developer ID for application codesigning (macOS only)' 21 | required: false 22 | default: '-' 23 | installerIdent: 24 | description: 'Developer ID for installer package codesigning (macOS only)' 25 | required: false 26 | default: '' 27 | codesignUser: 28 | description: 'Apple ID username for notarization (macOS only)' 29 | required: false 30 | default: '' 31 | codesignPass: 32 | description: 'Apple ID password for notarization (macOS only)' 33 | required: false 34 | default: '' 35 | createInstaller: 36 | description: 'Create InnoSetup installer (Windows only)' 37 | required: false 38 | default: 'false' 39 | workingDirectory: 40 | description: 'Working directory for packaging' 41 | required: false 42 | default: ${{ github.workspace }} 43 | runs: 44 | using: 'composite' 45 | steps: 46 | - name: Run macOS packaging 47 | if: ${{ runner.os == 'macOS' }} 48 | shell: zsh {0} 49 | env: 50 | CODESIGN_IDENT: ${{ inputs.codesignIdent }} 51 | CODESIGN_IDENT_INSTALLER: ${{ inputs.installerIdent }} 52 | CODESIGN_IDENT_USER: ${{ inputs.codesignUser }} 53 | CODESIGN_IDENT_PASS: ${{ inputs.codesignPass }} 54 | run: | 55 | package_args=( 56 | -c ${{ inputs.config }} 57 | -t macos-${{ inputs.target }} 58 | ) 59 | 60 | if [[ '${{ inputs.codesign }}' == 'true' ]] package_args+=(-s) 61 | if [[ '${{ inputs.notarize }}' == 'true' ]] package_args+=(-n) 62 | if (( ${+CI} && ${+RUNNER_DEBUG} )) build_args+=(--debug) 63 | 64 | ${{ inputs.workingDirectory }}/.github/scripts/package-macos.zsh ${package_args} 65 | 66 | - name: Run Linux packaging 67 | if: ${{ runner.os == 'Linux' }} 68 | shell: bash 69 | run: | 70 | package_args=( 71 | -c ${{ inputs.config }} 72 | -t linux-${{ inputs.target }} 73 | ) 74 | if [[ -n "${CI}" && -n "${RUNNER_DEBUG}" ]]; then 75 | build_args+=(--debug) 76 | fi 77 | 78 | ${{ inputs.workingDirectory }}/.github/scripts/package-linux.sh "${package_args[@]}" 79 | 80 | - name: Run Windows packaging 81 | if: ${{ runner.os == 'Windows' }} 82 | shell: pwsh 83 | run: | 84 | $PackageArgs = @{ 85 | Target = '${{ inputs.target }}' 86 | Configuration = '${{ inputs.config }}' 87 | } 88 | 89 | if ( '${{ inputs.createInstaller }}' -eq 'true' ) { 90 | $PackageArgs += @{BuildInstaller = $true} 91 | } 92 | 93 | if ( ( Test-Path env:CI ) -and ( Test-Path env:RUNNER_DEBUG ) ) { 94 | $BuildArgs += @{ 95 | Debug = $true 96 | } 97 | } 98 | 99 | ${{ inputs.workingDirectory }}/.github/scripts/Package-Windows.ps1 @PackageArgs 100 | -------------------------------------------------------------------------------- /data/shaders/boom-so-much-mask.effect: -------------------------------------------------------------------------------- 1 | #include "common.effect" 2 | 3 | uniform float4x4 ViewProj; 4 | 5 | uniform texture2d image; // What we're masking 6 | uniform texture2d buffer; // Last frame to add next to 7 | uniform texture2d current_input_mask; // Source that creates the mask. 8 | uniform texture2d adjustment_mask; // Mask for applying adjustments. 9 | 10 | uniform float alpha_reduction; 11 | 12 | uniform float min_brightness; 13 | uniform float max_brightness; 14 | uniform float min_contrast; 15 | uniform float max_contrast; 16 | uniform float min_saturation; 17 | uniform float max_saturation; 18 | uniform float min_hue_shift; 19 | uniform float max_hue_shift; 20 | 21 | sampler_state textureSampler{ 22 | Filter = Linear; 23 | AddressU = Clamp; 24 | AddressV = Clamp; 25 | MinLOD = 0; 26 | MaxLOD = 0; 27 | }; 28 | 29 | struct VertData 30 | { 31 | float4 pos : POSITION; 32 | float2 uv : TEXCOORD0; 33 | }; 34 | 35 | 36 | VertData mainTransform(VertData v_in) 37 | { 38 | v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); 39 | return v_in; 40 | } 41 | 42 | float4 alphaMaskImage(VertData v_in) : TARGET 43 | { 44 | float4 buffer_color = buffer.Sample(textureSampler, v_in.uv); 45 | float4 cur_color = current_input_mask.Sample(textureSampler, v_in.uv); 46 | buffer_color.a = max(0.0, buffer_color.a - alpha_reduction); 47 | float alpha_mask = max(buffer_color.a, cur_color.a); 48 | return float4(1.0, 1.0, 1.0, alpha_mask); 49 | } 50 | 51 | float4 alphaImage(VertData v_in) : TARGET 52 | { 53 | float4 buffer_color = buffer.Sample(textureSampler, v_in.uv); 54 | float4 cur_color = current_input_mask.Sample(textureSampler, v_in.uv); 55 | buffer_color.a = max(0.0, buffer_color.a - alpha_reduction); 56 | float alpha_mask = max(buffer_color.a, cur_color.a); 57 | float4 c = image.Sample(textureSampler, v_in.uv); 58 | return float4(c.rgb, c.a * alpha_mask); 59 | } 60 | 61 | float4 adjustmentsImage(VertData v_in) : TARGET 62 | { 63 | float4 buffer_color = adjustment_mask.Sample(textureSampler, v_in.uv); 64 | float alpha_mask = buffer_color.a; 65 | float4 color = image.Sample(textureSampler, v_in.uv); 66 | color = adjustments( 67 | color, 68 | lerp(min_brightness, max_brightness, alpha_mask), 69 | lerp(min_contrast, max_contrast, alpha_mask), 70 | lerp(min_saturation, max_saturation, alpha_mask), 71 | lerp(min_hue_shift, max_hue_shift, alpha_mask) 72 | ); 73 | return color; 74 | } 75 | 76 | float4 alphaImageFreeze(VertData v_in) : TARGET 77 | { 78 | float4 buffer_color = buffer.Sample(textureSampler, v_in.uv); 79 | float4 cur_color = current_input_mask.Sample(textureSampler, v_in.uv); 80 | buffer_color.a = max(0.0, buffer_color.a - alpha_reduction);; 81 | float4 c = image.Sample(textureSampler, v_in.uv); 82 | 83 | return cur_color.a > 0.01 ? c : buffer_color; 84 | } 85 | 86 | technique Alpha 87 | { 88 | pass 89 | { 90 | vertex_shader = mainTransform(v_in); 91 | pixel_shader = alphaImage(v_in); 92 | } 93 | } 94 | 95 | technique AlphaFreeze 96 | { 97 | pass 98 | { 99 | vertex_shader = mainTransform(v_in); 100 | pixel_shader = alphaImageFreeze(v_in); 101 | } 102 | } 103 | 104 | technique Mask 105 | { 106 | pass 107 | { 108 | vertex_shader = mainTransform(v_in); 109 | pixel_shader = alphaMaskImage(v_in); 110 | } 111 | } 112 | 113 | technique Adjustments 114 | { 115 | pass 116 | { 117 | vertex_shader = mainTransform(v_in); 118 | pixel_shader = adjustmentsImage(v_in); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /data/shaders/svg-mask.effect: -------------------------------------------------------------------------------- 1 | #include "common.effect" 2 | 3 | uniform float4x4 ViewProj; 4 | 5 | uniform texture2d image; 6 | uniform float2 uv_size; 7 | uniform texture2d svg_image; 8 | uniform float2 svg_uv_size; 9 | uniform float2 offset; 10 | uniform float2 anchor; 11 | uniform float primary_alpha; 12 | uniform float secondary_alpha; 13 | uniform float invert; 14 | uniform float4x4 rotation_matrix; 15 | 16 | uniform float min_brightness; 17 | uniform float max_brightness; 18 | uniform float min_contrast; 19 | uniform float max_contrast; 20 | uniform float min_saturation; 21 | uniform float max_saturation; 22 | uniform float min_hue_shift; 23 | uniform float max_hue_shift; 24 | 25 | 26 | sampler_state textureSampler{ 27 | Filter = Linear; 28 | AddressU = Clamp; 29 | AddressV = Clamp; 30 | }; 31 | 32 | sampler_state svgSampler { 33 | Filter = Linear; 34 | AddressU = Border; 35 | AddressV = Border; 36 | BorderColor = 0x00000000; 37 | }; 38 | 39 | struct VertData 40 | { 41 | float4 pos : POSITION; 42 | float2 uv : TEXCOORD0; 43 | }; 44 | 45 | VertData mainTransform(VertData v_in) 46 | { 47 | v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); 48 | return v_in; 49 | } 50 | 51 | float4 mainImage(VertData v_in) : TARGET 52 | { 53 | float2 coord = v_in.uv * uv_size; 54 | float2 uv_svg = (coord - offset + svg_uv_size/2.0f) / svg_uv_size; 55 | float4 svg = svg_image.Sample(svgSampler, uv_svg); 56 | float4 c = image.Sample(textureSampler, v_in.uv); 57 | c.a = c.a * svg.a; 58 | return c; 59 | } 60 | 61 | float4 faImage(VertData v_in) : TARGET 62 | { 63 | float2 coord = v_in.uv * uv_size; 64 | 65 | float2 coord_p = coord - offset + anchor * svg_uv_size; 66 | float2 coord_rot = mul(rotation_matrix, float4(coord_p, 1.0f, 1.0f)).xy; 67 | 68 | float2 uv_svg = coord_rot / svg_uv_size; 69 | float4 svg = svg_image.Sample(svgSampler, uv_svg); 70 | float4 c = image.Sample(textureSampler, v_in.uv); 71 | svg.a = svg.a * (svg.r * (primary_alpha - secondary_alpha) + secondary_alpha); 72 | svg.a = lerp(svg.a, (1.0 - svg.a), invert); 73 | c.a = c.a * svg.a; 74 | return c; 75 | } 76 | 77 | float4 faAdjustmentsImage(VertData v_in) : TARGET 78 | { 79 | float2 coord = v_in.uv * uv_size; 80 | 81 | float2 coord_p = coord - offset + anchor * svg_uv_size; 82 | float2 coord_rot = mul(rotation_matrix, float4(coord_p, 1.0f, 1.0f)).xy; 83 | 84 | float2 uv_svg = coord_rot / svg_uv_size; 85 | float4 svg = svg_image.Sample(svgSampler, uv_svg); 86 | float4 c = image.Sample(textureSampler, v_in.uv); 87 | svg.a = svg.a * (svg.r * (primary_alpha - secondary_alpha) + secondary_alpha); 88 | svg.a = lerp(svg.a, (1.0 - svg.a), invert); 89 | //c.a = c.a * svg.a; 90 | float4 color_adj_min = adjustments( 91 | c, 92 | min_brightness, 93 | min_contrast, 94 | min_saturation, 95 | min_hue_shift 96 | ); 97 | float4 color_adj_max = adjustments( 98 | c, 99 | max_brightness, 100 | max_contrast, 101 | max_saturation, 102 | max_hue_shift 103 | ); 104 | return (color_adj_min * (1.0 - svg.a) + color_adj_max * svg.a); 105 | } 106 | 107 | technique Draw 108 | { 109 | pass 110 | { 111 | vertex_shader = mainTransform(v_in); 112 | pixel_shader = mainImage(v_in); 113 | } 114 | } 115 | 116 | 117 | technique DrawFA 118 | { 119 | pass 120 | { 121 | vertex_shader = mainTransform(v_in); 122 | pixel_shader = faImage(v_in); 123 | } 124 | } 125 | 126 | technique DrawFAAdjustments 127 | { 128 | pass 129 | { 130 | vertex_shader = mainTransform(v_in); 131 | pixel_shader = faAdjustmentsImage(v_in); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /.github/scripts/Build-Windows.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param( 3 | [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] 4 | [string] $Configuration = 'RelWithDebInfo', 5 | [ValidateSet('x86', 'x64')] 6 | [string] $Target, 7 | [ValidateSet('Visual Studio 17 2022', 'Visual Studio 16 2019')] 8 | [string] $CMakeGenerator, 9 | [switch] $SkipAll, 10 | [switch] $SkipBuild, 11 | [switch] $SkipDeps, 12 | [switch] $SkipUnpack 13 | ) 14 | 15 | $ErrorActionPreference = 'Stop' 16 | 17 | if ( $DebugPreference -eq 'Continue' ) { 18 | $VerbosePreference = 'Continue' 19 | $InformationPreference = 'Continue' 20 | } 21 | 22 | if ( $PSVersionTable.PSVersion -lt '7.0.0' ) { 23 | Write-Warning 'The obs-deps PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6' 24 | exit 2 25 | } 26 | 27 | function Build { 28 | trap { 29 | Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue' 30 | Write-Error $_ 31 | exit 2 32 | } 33 | 34 | $ScriptHome = $PSScriptRoot 35 | $ProjectRoot = Resolve-Path -Path "$PSScriptRoot/../.." 36 | $BuildSpecFile = "${ProjectRoot}/buildspec.json" 37 | 38 | $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse 39 | 40 | foreach($Utility in $UtilityFunctions) { 41 | Write-Debug "Loading $($Utility.FullName)" 42 | . $Utility.FullName 43 | } 44 | 45 | $BuildSpec = Get-Content -Path ${BuildSpecFile} -Raw | ConvertFrom-Json 46 | $ProductName = $BuildSpec.name 47 | $ProductVersion = $BuildSpec.version 48 | 49 | $script:DepsVersion = '' 50 | $script:QtVersion = '5' 51 | $script:VisualStudioVersion = '' 52 | $script:PlatformSDK = '10.0.18363.657' 53 | 54 | Setup-Host 55 | 56 | if ( $CmakeGenerator -eq '' ) { 57 | $CmakeGenerator = $script:VisualStudioVersion 58 | } 59 | 60 | (Get-Content -Path ${ProjectRoot}/CMakeLists.txt -Raw) ` 61 | -replace "project\((.*) VERSION (.*)\)", "project(${ProductName} VERSION ${ProductVersion})" ` 62 | | Out-File -Path ${ProjectRoot}/CMakeLists.txt 63 | 64 | Setup-Obs 65 | 66 | Push-Location -Stack BuildTemp 67 | if ( ! ( ( $SkipAll ) -or ( $SkipBuild ) ) ) { 68 | Ensure-Location $ProjectRoot 69 | 70 | $DepsPath = "plugin-deps-${script:DepsVersion}-qt${script:QtVersion}-${script:Target}" 71 | $CmakeArgs = @( 72 | '-G', $CmakeGenerator 73 | "-DCMAKE_SYSTEM_VERSION=${script:PlatformSDK}" 74 | "-DCMAKE_GENERATOR_PLATFORM=$(if (${script:Target} -eq "x86") { "Win32" } else { "x64" })" 75 | "-DCMAKE_BUILD_TYPE=${Configuration}" 76 | "-DCMAKE_PREFIX_PATH:PATH=$(Resolve-Path -Path "${ProjectRoot}/../obs-build-dependencies/${DepsPath}")" 77 | "-DQT_VERSION=${script:QtVersion}" 78 | ) 79 | 80 | Log-Debug "Attempting to configure OBS with CMake arguments: $($CmakeArgs | Out-String)" 81 | Log-Information "Configuring ${ProductName}..." 82 | Invoke-External cmake -S . -B build_${script:Target} @CmakeArgs 83 | 84 | $CmakeArgs = @( 85 | '--config', "${Configuration}" 86 | ) 87 | 88 | if ( $VerbosePreference -eq 'Continue' ) { 89 | $CmakeArgs+=('--verbose') 90 | } 91 | 92 | Log-Information "Building ${ProductName}..." 93 | Invoke-External cmake --build "build_${script:Target}" @CmakeArgs 94 | } 95 | Log-Information "Install ${ProductName}..." 96 | Invoke-External cmake --install "build_${script:Target}" --prefix "${ProjectRoot}/release" @CmakeArgs 97 | 98 | Pop-Location -Stack BuildTemp 99 | } 100 | 101 | Build 102 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # please use clang-format version 8 or later 2 | 3 | Standard: Cpp11 4 | AccessModifierOffset: -8 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | #AllowAllArgumentsOnNextLine: false # requires clang-format 9 12 | #AllowAllConstructorInitializersOnNextLine: false # requires clang-format 9 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortBlocksOnASingleLine: false 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: Inline 17 | AllowShortIfStatementsOnASingleLine: false 18 | #AllowShortLambdasOnASingleLine: Inline # requires clang-format 9 19 | AllowShortLoopsOnASingleLine: false 20 | AlwaysBreakAfterDefinitionReturnType: None 21 | AlwaysBreakAfterReturnType: None 22 | AlwaysBreakBeforeMultilineStrings: false 23 | AlwaysBreakTemplateDeclarations: false 24 | BinPackArguments: true 25 | BinPackParameters: true 26 | BraceWrapping: 27 | AfterClass: false 28 | AfterControlStatement: false 29 | AfterEnum: false 30 | AfterFunction: true 31 | AfterNamespace: false 32 | AfterObjCDeclaration: false 33 | AfterStruct: false 34 | AfterUnion: false 35 | AfterExternBlock: false 36 | BeforeCatch: false 37 | BeforeElse: false 38 | IndentBraces: false 39 | SplitEmptyFunction: true 40 | SplitEmptyRecord: true 41 | SplitEmptyNamespace: true 42 | BreakBeforeBinaryOperators: None 43 | BreakBeforeBraces: Custom 44 | BreakBeforeTernaryOperators: true 45 | BreakConstructorInitializers: BeforeColon 46 | BreakStringLiterals: false # apparently unpredictable 47 | ColumnLimit: 80 48 | CompactNamespaces: false 49 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 50 | ConstructorInitializerIndentWidth: 8 51 | ContinuationIndentWidth: 8 52 | Cpp11BracedListStyle: true 53 | DerivePointerAlignment: false 54 | DisableFormat: false 55 | FixNamespaceComments: false 56 | ForEachMacros: 57 | - 'json_object_foreach' 58 | - 'json_object_foreach_safe' 59 | - 'json_array_foreach' 60 | - 'HASH_ITER' 61 | IncludeBlocks: Preserve 62 | IndentCaseLabels: false 63 | IndentPPDirectives: None 64 | IndentWidth: 8 65 | IndentWrappedFunctionNames: false 66 | KeepEmptyLinesAtTheStartOfBlocks: true 67 | MaxEmptyLinesToKeep: 1 68 | NamespaceIndentation: None 69 | #ObjCBinPackProtocolList: Auto # requires clang-format 7 70 | ObjCBlockIndentWidth: 8 71 | ObjCSpaceAfterProperty: true 72 | ObjCSpaceBeforeProtocolList: true 73 | 74 | PenaltyBreakAssignment: 10 75 | PenaltyBreakBeforeFirstCallParameter: 30 76 | PenaltyBreakComment: 10 77 | PenaltyBreakFirstLessLess: 0 78 | PenaltyBreakString: 10 79 | PenaltyExcessCharacter: 100 80 | PenaltyReturnTypeOnItsOwnLine: 60 81 | 82 | PointerAlignment: Right 83 | ReflowComments: false 84 | SortIncludes: false 85 | SortUsingDeclarations: false 86 | SpaceAfterCStyleCast: false 87 | #SpaceAfterLogicalNot: false # requires clang-format 9 88 | SpaceAfterTemplateKeyword: false 89 | SpaceBeforeAssignmentOperators: true 90 | #SpaceBeforeCtorInitializerColon: true # requires clang-format 7 91 | #SpaceBeforeInheritanceColon: true # requires clang-format 7 92 | SpaceBeforeParens: ControlStatements 93 | #SpaceBeforeRangeBasedForLoopColon: true # requires clang-format 7 94 | SpaceInEmptyParentheses: false 95 | SpacesBeforeTrailingComments: 1 96 | SpacesInAngles: false 97 | SpacesInCStyleCastParentheses: false 98 | SpacesInContainerLiterals: false 99 | SpacesInParentheses: false 100 | SpacesInSquareBrackets: false 101 | #StatementMacros: # requires clang-format 8 102 | # - 'Q_OBJECT' 103 | TabWidth: 8 104 | #TypenameMacros: # requires clang-format 9 105 | # - 'DARRAY' 106 | UseTab: ForContinuationAndIndentation 107 | --- 108 | Language: ObjC 109 | -------------------------------------------------------------------------------- /data/shaders/chroma-key.effect: -------------------------------------------------------------------------------- 1 | uniform float4x4 ViewProj; 2 | uniform texture2d image; 3 | 4 | uniform float4 cb_v4 = { -0.100644, -0.338572, 0.439216, 0.501961 }; 5 | uniform float4 cr_v4 = { 0.439216, -0.398942, -0.040274, 0.501961 }; 6 | 7 | uniform float opacity; 8 | uniform float contrast; 9 | uniform float brightness; 10 | uniform float gamma; 11 | 12 | uniform float2 chroma_key; 13 | uniform float2 pixel_size; 14 | uniform float similarity; 15 | uniform float smoothness; 16 | uniform float spill; 17 | 18 | sampler_state textureSampler { 19 | Filter = Linear; 20 | AddressU = Clamp; 21 | AddressV = Clamp; 22 | }; 23 | 24 | struct VertData { 25 | float4 pos : POSITION; 26 | float2 uv : TEXCOORD0; 27 | }; 28 | 29 | VertData VSDefault(VertData v_in) 30 | { 31 | VertData vert_out; 32 | vert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); 33 | vert_out.uv = v_in.uv; 34 | return vert_out; 35 | } 36 | 37 | float4 CalcColor(float4 rgba) 38 | { 39 | return float4(pow(rgba.rgb, float3(gamma, gamma, gamma)) * contrast + brightness, rgba.a); 40 | } 41 | 42 | float GetChromaDist(float3 rgb) 43 | { 44 | float cb = dot(rgb.rgb, cb_v4.xyz) + cb_v4.w; 45 | float cr = dot(rgb.rgb, cr_v4.xyz) + cr_v4.w; 46 | return distance(chroma_key, float2(cb, cr)); 47 | } 48 | 49 | float GetNonlinearChannel(float u) 50 | { 51 | return (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1.0 / 2.4)) - 0.055); 52 | } 53 | 54 | float3 GetNonlinearColor(float3 rgb) 55 | { 56 | return float3(GetNonlinearChannel(rgb.r), GetNonlinearChannel(rgb.g), GetNonlinearChannel(rgb.b)); 57 | } 58 | 59 | float3 SampleTexture(float2 uv) 60 | { 61 | float3 rgb = image.Sample(textureSampler, uv).rgb; 62 | return GetNonlinearColor(rgb); 63 | } 64 | 65 | float GetBoxFilteredChromaDist(float3 rgb, float2 texCoord) 66 | { 67 | float2 h_pixel_size = pixel_size / 2.0; 68 | float2 point_0 = float2(pixel_size.x, h_pixel_size.y); 69 | float2 point_1 = float2(h_pixel_size.x, -pixel_size.y); 70 | float distVal = GetChromaDist(SampleTexture(texCoord-point_0)); 71 | distVal += GetChromaDist(SampleTexture(texCoord+point_0)); 72 | distVal += GetChromaDist(SampleTexture(texCoord-point_1)); 73 | distVal += GetChromaDist(SampleTexture(texCoord+point_1)); 74 | distVal *= 2.0; 75 | distVal += GetChromaDist(GetNonlinearColor(rgb)); 76 | return distVal / 9.0; 77 | } 78 | 79 | float4 ProcessChromaKey(float4 rgba, VertData v_in) 80 | { 81 | float chromaDist = GetBoxFilteredChromaDist(rgba.rgb, v_in.uv); 82 | float baseMask = chromaDist - similarity; 83 | float fullMask = pow(saturate(baseMask / smoothness), 1.5); 84 | float spillVal = pow(saturate(baseMask / spill), 1.5); 85 | 86 | rgba.a *= opacity; 87 | rgba.a *= fullMask; 88 | 89 | float desat = dot(rgba.rgb, float3(0.2126, 0.7152, 0.0722)); 90 | rgba.rgb = lerp(float3(desat, desat, desat), rgba.rgb, spillVal); 91 | 92 | return CalcColor(rgba); 93 | } 94 | 95 | float4 PSChromaKeyRGBA(VertData v_in) : TARGET 96 | { 97 | float4 rgba = image.Sample(textureSampler, v_in.uv); 98 | rgba.rgb *= (rgba.a > 0.) ? (1. / rgba.a) : 0.; 99 | rgba = ProcessChromaKey(rgba, v_in); 100 | rgba.rgb *= rgba.a; 101 | return rgba; 102 | } 103 | 104 | float4 PSChromaKeyRGBAMatte(VertData v_in) : TARGET 105 | { 106 | float4 rgba = image.Sample(textureSampler, v_in.uv); 107 | rgba.rgb *= (rgba.a > 0.) ? (1. / rgba.a) : 0.; 108 | rgba = ProcessChromaKey(rgba, v_in); 109 | float matte = (1.0 - rgba.a); 110 | return float4(matte, matte, matte, 1.0); 111 | } 112 | 113 | technique Draw 114 | { 115 | pass 116 | { 117 | vertex_shader = VSDefault(v_in); 118 | pixel_shader = PSChromaKeyRGBA(v_in); 119 | } 120 | } 121 | 122 | technique DrawMatte 123 | { 124 | pass 125 | { 126 | vertex_shader = VSDefault(v_in); 127 | pixel_shader = PSChromaKeyRGBAMatte(v_in); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /data/shaders/circle-mask.effect: -------------------------------------------------------------------------------- 1 | #include "common.effect" 2 | 3 | uniform float4x4 ViewProj; 4 | 5 | uniform texture2d image; 6 | uniform float2 uv_size; 7 | uniform float2 mask_position; 8 | uniform float2 global_position; 9 | uniform float global_scale; 10 | uniform float radius; 11 | uniform float zoom; 12 | uniform float feather_amount; 13 | uniform float alpha_zero; 14 | uniform float invert; 15 | 16 | uniform float min_brightness; 17 | uniform float max_brightness; 18 | uniform float min_contrast; 19 | uniform float max_contrast; 20 | uniform float min_saturation; 21 | uniform float max_saturation; 22 | uniform float min_hue_shift; 23 | uniform float max_hue_shift; 24 | 25 | sampler_state textureSampler{ 26 | Filter = Linear; 27 | AddressU = Border; 28 | AddressV = Border; 29 | BorderColor = 0x00000000; 30 | }; 31 | struct VertData 32 | { 33 | float4 pos : POSITION; 34 | float2 uv : TEXCOORD0; 35 | }; 36 | 37 | float SDF(float2 coord, float radius) 38 | { 39 | return length(coord) - radius; 40 | } 41 | 42 | VertData mainTransform(VertData v_in) 43 | { 44 | v_in.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj); 45 | return v_in; 46 | } 47 | 48 | float4 alphaImage(VertData v_in) : TARGET 49 | { 50 | float zero = saturate(floor(radius)); 51 | float2 coord = v_in.uv * uv_size; 52 | float2 shift = global_position - mask_position; 53 | float2 coord_p = coord - shift; 54 | float2 dist = coord_p - mask_position; // coord - global_position + mask_position - mask_position 55 | float d = SDF(dist, radius); 56 | d = feather_amount > 0.0f ? smoothstep(0.0f, feather_amount, -d + feather_amount) : saturate(-(d - 1.0) * zero); 57 | 58 | float4 color = image.Sample(textureSampler, (mask_position + dist / zoom / global_scale) / uv_size); 59 | return float4(color.rgb, (1.0f - invert) * color.a * d + invert * color.a * (1.0f - d)); 60 | } 61 | 62 | float4 alphaFrameCheckImage(VertData v_in) : TARGET 63 | { 64 | float zero = saturate(floor(radius)); 65 | float2 coord = v_in.uv * uv_size; 66 | float2 shift = global_position - mask_position; 67 | float2 coord_p = coord - shift; 68 | float2 dist = coord_p - mask_position; 69 | float d = SDF(dist, radius); 70 | float2 sample_uv = (mask_position + dist / zoom / global_scale) / uv_size; 71 | float alpha_scale = sample_uv.x >= 0.0f && sample_uv.x <= 1.0f && 72 | sample_uv.y >= 0.0f && sample_uv.y <= 1.0f ? 1.0f : 0.0f; 73 | d = feather_amount > 0.0f ? smoothstep(0.0f, feather_amount, -d + feather_amount) : saturate(-(d - 1.0) * zero); 74 | d = (1.0f - invert) * d + invert * (1.0 - d); 75 | d = (d + alpha_zero) / (1.0 + alpha_zero); 76 | 77 | float4 color = image.Sample(textureSampler, (mask_position + dist / zoom / global_scale) / uv_size); 78 | return float4(color.rgb, color.a * d); 79 | } 80 | 81 | float4 adjustmentsImage(VertData v_in) : TARGET 82 | { 83 | float zero = saturate(floor(radius)); 84 | float2 coord = v_in.uv * uv_size; 85 | float2 dist = coord - mask_position; 86 | float d = SDF(dist, radius); 87 | d = feather_amount > 0.0f ? smoothstep(0.0f, feather_amount, -d + feather_amount) : saturate(-(d - 1.0) * zero); 88 | d = (1.0f - invert) * d + invert * (1.0 - d); 89 | 90 | float4 color = image.Sample(textureSampler, v_in.uv); 91 | float4 color_adj_min = adjustments( 92 | color, 93 | min_brightness, 94 | min_contrast, 95 | min_saturation, 96 | min_hue_shift 97 | ); 98 | float4 color_adj_max = adjustments( 99 | color, 100 | max_brightness, 101 | max_contrast, 102 | max_saturation, 103 | max_hue_shift 104 | ); 105 | return (color_adj_min * (1.0 - d) + color_adj_max * d); 106 | } 107 | 108 | technique Alpha 109 | { 110 | pass 111 | { 112 | vertex_shader = mainTransform(v_in); 113 | pixel_shader = alphaImage(v_in); 114 | } 115 | } 116 | 117 | technique AlphaFrameCheck 118 | { 119 | pass 120 | { 121 | vertex_shader = mainTransform(v_in); 122 | pixel_shader = alphaFrameCheckImage(v_in); 123 | } 124 | } 125 | 126 | technique Adjustments 127 | { 128 | pass 129 | { 130 | vertex_shader = mainTransform(v_in); 131 | pixel_shader = adjustmentsImage(v_in); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /.github/scripts/utils.pwsh/Invoke-GitCheckout.ps1: -------------------------------------------------------------------------------- 1 | function Set-GitConfig { 2 | <# 3 | .SYNOPSIS 4 | Sets a git config value. 5 | .DESCRIPTION 6 | Allows setting single or multiple config values in a PowerShell-friendly fashion. 7 | .EXAMPLE 8 | Set-GitConfig advice.detachedHead false 9 | #> 10 | 11 | if ( $args.Count -lt 2 ) { 12 | throw 'Set-GitConfig called without required arguments