Skip to content

Instantly share code, notes, and snippets.

@nikolay-n
Created November 5, 2020 18:10
Show Gist options
  • Save nikolay-n/a0f8b5d645ba74a3f30655790550496b to your computer and use it in GitHub Desktop.
Save nikolay-n/a0f8b5d645ba74a3f30655790550496b to your computer and use it in GitHub Desktop.
Screen glitch example
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
import Foundation
import AppKit
import WebKit
import Quartz
import ctypes
from PyObjCTools import AppHelper
from objc import _objc, nil, super, pyobjc_unicode, registerMetaDataForSelector
import shutil
import sys
import os
import tempfile as tmp
if len(sys.argv) != 2 or not os.path.exists(sys.argv[1]):
print("Usage: python2.7 ./glitch.py /path/to/image");
sys.exit(1)
def hide_cursor():
Quartz.CGAssociateMouseAndMouseCursorPosition(False)
Quartz.CGDisplayHideCursor(Quartz.CGMainDisplayID())
def show_cursor():
Quartz.CGAssociateMouseAndMouseCursorPosition(True)
Quartz.CGDisplayShowCursor(Quartz.CGMainDisplayID())
class ProcessSerialNumber(ctypes.Structure):
_fields_ = [
('highLongOfPSN', ctypes.c_uint32),
('lowLongOfPSN', ctypes.c_uint32),
]
kNoProcess = 0
kSystemProcess = 1
kCurrentProcess = 2
kProcessTransformToForegroundApplication = 1
kProcessTransformToBackgroundApplication = 2
kProcessTransformToUIElementAppication = 4
appSrvc = ctypes.CDLL('/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices')
appSrvc.TransformProcessType.argtypes = [ctypes.POINTER(ProcessSerialNumber), ctypes.c_uint32]
appSrvc.SetFrontProcess.argtypes = [ctypes.POINTER(ProcessSerialNumber)]
psn = ProcessSerialNumber(0, kCurrentProcess)
appSrvc.TransformProcessType(psn, kProcessTransformToForegroundApplication)
appSrvc.SetFrontProcess(psn)
sharedApp = AppKit.NSApplication.sharedApplication()
currentApp = AppKit.NSRunningApplication.currentApplication()
# hide from Dock
sharedApp.setActivationPolicy_(AppKit.NSApplicationActivationPolicyAccessory)
bundle = AppKit.NSBundle.mainBundle()
info = bundle.localizedInfoDictionary() or bundle.infoDictionary()
info['CFBundleName'] = 'WebKit'
info['NSAppTransportSecurity'] = {'NSAllowsArbitraryLoads': Foundation.YES}
_objc_so = ctypes.cdll.LoadLibrary(_objc.__file__)
# Bridgesupport metadata for [WKWebView evaluateJavaScript:completionHandler:]
_eval_js_metadata = { 'arguments': { 3: { 'callable': { 'retval': { 'type': b'v' }, 'arguments': { 0: { 'type': b'^v' }, 1: { 'type': b'@' }, 2: { 'type': b'@' }}}}}}
class MainWindow(AppKit.NSWindow):
def canBecomeKeyWindow(self):
return True
def canBecomeMainWindow(self):
return False
rect = AppKit.NSScreen.mainScreen().frame()
window_mask = AppKit.NSBorderlessWindowMask
window = MainWindow.alloc().\
initWithContentRect_styleMask_backing_defer_(rect, window_mask, AppKit.NSBackingStoreBuffered, False).retain()
window.setAnimationBehavior_(AppKit.NSWindowAnimationBehaviorNone)
frame = window.frame()
frame.size.width = rect.size.width
frame.size.height = rect.size.height
window.setFrame_display_(frame, True)
webkit = WebKit.WKWebView.alloc().initWithFrame_(rect).retain()
window.setOpaque_(False)
window.setHasShadow_(False)
window.setBackgroundColor_(AppKit.NSColor.colorWithSRGBRed_green_blue_alpha_(0, 0, 0, 1))
window.setAlphaValue_(0.0)
window.setLevel_(AppKit.NSScreenSaverWindowLevel + 1)
window.setCollectionBehavior_(1 << 7) # NSWindowCollectionBehaviorFullScreenPrimary
try:
webkit.evaluateJavaScript_completionHandler_('', lambda a, b: None)
except TypeError:
registerMetaDataForSelector(b'WKWebView', b'evaluateJavaScript:completionHandler:', _eval_js_metadata)
config = webkit.configuration()
try:
config.preferences().setValue_forKey_(Foundation.NO, 'backspaceKeyNavigationEnabled')
config.preferences().setValue_forKey_(Foundation.YES, 'allowFileAccessFromFileURLs')
except Exception as e:
pass
tmp_dir = tmp.mkdtemp()
index_path = os.path.join(tmp_dir, "index.html")
image_file = sys.argv[1]
image_name = os.path.basename(image_file)
image_path = os.path.join(tmp_dir, image_name)
shutil.copy(image_file, image_path)
index_url = Foundation.NSURL.fileURLWithPath_(index_path)
base_url = Foundation.NSURL.fileURLWithPath_(tmp_dir)
content='''<html>
<head>
<style>
html, body {
padding:0px;
margin:0px;
}
body {
width:100%;
height:100%;
overflow: hidden;
background: #000;
color:#fff;
font-family: Monaco;
font-size:11pt;
-webkit-user-select: none;
user-select: none;
cursor: default;
}
#gl {
width: 100%;
height:100%;
}
</style>
<script>
var fps = 40;
const defaultShaderType = [
'VERTEX_SHADER',
'FRAGMENT_SHADER',
];
const screenShot = "__IMAGE__";
const vertexSrc =`
attribute vec4 a_position;
attribute vec2 a_texcoord;
//uniform vec2 u_resolution;
varying vec2 v_texcoord;
void main() {
gl_Position = a_position;
v_texcoord = a_texcoord;
}
`;
const fragmentSrc=`
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_texture;
uniform vec2 u_resolution;
uniform float u_time;
uniform float random_seed;
const float glitch_size = .9;
const vec2 offset = vec2(0.0, 0.0);
const float glitch_horizontal = 0.1;
const float glitch_vertical = 0.01;
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void main() {
vec2 xy = gl_FragCoord.xy / u_resolution.xy;
vec4 original = texture2D(u_texture, v_texcoord);
vec4 resultColor;
bool shifted = false;
float randSeed = (random_seed == 0.0) ? u_time : random_seed;
vec2 random;
float local_glitch_size = glitch_size;
float random_offset = 0.0;
if (local_glitch_size > 0.0) {
random.x = rand(vec2(floor(random_offset + xy.y / local_glitch_size) * local_glitch_size, randSeed));
random.y = rand(vec2(floor(random_offset + xy.x / local_glitch_size) * local_glitch_size, randSeed));
} else {
random.x = rand(vec2(xy.x, randSeed));
random.y = rand(vec2(xy.y, randSeed));
}
vec2 shift;
// if doing a horizontal glitch do a random shift
if ((random.x < glitch_horizontal)&&(random.y < glitch_vertical)) {
shift = (offset / u_resolution - 0.5);
shift = shift * rand(shift + random);
xy.x = mod(xy.x + random.x, 1.0);
xy.y = mod(xy.y + random.y, 1.0);
xy = xy + shift;
shifted = true;
}
else if (random.x < glitch_horizontal) {
shift = (offset / u_resolution - 0.5);
shift = shift * rand(shift + random);
xy = mod(xy + vec2(0.0, random.x) + shift, 1.0);
shifted = true;
}
else if (random.y < glitch_vertical) {
shift = (offset / u_resolution - 0.5);
shift = shift * rand(shift + random);
xy = mod(xy + vec2(random.y, 0.0) + shift, 1.0);
shifted = true;
}
resultColor = texture2D(u_texture, xy);
//resultColor = max(resultColor, original);
int rnd = int(floor(12.0 * rand(vec2(randSeed, resultColor.g))));
if (rnd == 1) {
resultColor = original * resultColor ;
resultColor = abs(original - resultColor) / resultColor * original - 0.5;
float r=rand(vec2(randSeed, resultColor.r));
float g=rand(vec2(randSeed, resultColor.g));
float b=rand(vec2(randSeed, resultColor.b));
resultColor = vec4(r ,g, b ,1.0);
//resultColor = vec4(0.,0.,0.,1.0);
}else if (rnd == 3 ){
//resultColor = min(resultColor, original) - 0.5;
}else if (rnd == 4 ){
//resultColor = abs(resultColor - original);
}else if (rnd == 5 ){
resultColor = max(resultColor, original);
}
gl_FragColor = resultColor;
}
`;
window.addEventListener('DOMContentLoaded', (event) => main());
window.addEventListener('click', (event) => postMessage("exit"));
document.addEventListener('contextmenu', event => event.preventDefault()); // disable right mouse click
function main(){
var canvas = document.querySelector("#gl");
var gl = canvas.getContext("webgl");
var program = createProgramFromSources(gl, [vertexSrc, fragmentSrc]);
var positionLocation = gl.getAttribLocation(program, "a_position");
var texcoordLocation = gl.getAttribLocation(program, "a_texcoord");
var textureLocation = gl.getUniformLocation(program, "u_texture");
var seedLocation = gl.getUniformLocation(program, "random_seed");
var resolutionUniformLocation = gl.getUniformLocation(program, "u_resolution");
var timeUniformLocation = gl.getUniformLocation(program, "u_time");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
-1, -1,
-1, 1,
1, -1,
1, -1,
-1, 1,
1, 1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
var texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
var texcoords = [
0, 0,
0, 1,
1, 0,
1, 0,
0, 1,
1, 1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW);
var texture = gl.createTexture();
var img = new Image();
var random_seed = 0.0;
img.addEventListener('load', function() {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
requestAnimationFrame(render);
setTimeout(function(){
postMessage("loaded");
}, 100);
});
img.src = screenShot;
function render(time) {
time *= 0.001; // convert to seconds
resizeCanvasToDisplaySize(gl.canvas);
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindTexture(gl.TEXTURE_2D, texture);
// Tell WebGL to use our shader program pair
gl.useProgram(program);
// Setup the attributes to pull data from our buffers
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.enableVertexAttribArray(texcoordLocation);
gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);
gl.uniform1i(textureLocation, 0);
gl.uniform1f(timeUniformLocation, time);
gl.uniform1f(seedLocation, random_seed);
gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6);
setTimeout(function(){
requestAnimationFrame(render);
}, 1000/fps);
}
}
function resizeCanvasToDisplaySize(canvas, multiplier) {
multiplier = multiplier || 1;
const width = canvas.clientWidth * multiplier | 0;
const height = canvas.clientHeight * multiplier | 0;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
return true;
}
return false;
}
function createProgramFromSources(
gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
const shaders = [];
for (let ii = 0; ii < shaderSources.length; ++ii) {
shaders.push(loadShader(
gl, shaderSources[ii], gl[defaultShaderType[ii]], opt_errorCallback));
}
return createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback);
}
function createProgram(
gl, shaders, opt_attribs, opt_locations, opt_errorCallback) {
const errFn = opt_errorCallback || error;
const program = gl.createProgram();
shaders.forEach(function(shader) {
gl.attachShader(program, shader);
});
if (opt_attribs) {
opt_attribs.forEach(function(attrib, ndx) {
gl.bindAttribLocation(
program,
opt_locations ? opt_locations[ndx] : ndx,
attrib);
});
}
gl.linkProgram(program);
// Check the link status
const linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
// something went wrong with the link
const lastError = gl.getProgramInfoLog(program);
errFn('Error in program linking:' + lastError);
gl.deleteProgram(program);
return null;
}
return program;
}
function loadShader(gl, shaderSource, shaderType, opt_errorCallback) {
const errFn = opt_errorCallback || error;
// Create the shader object
const shader = gl.createShader(shaderType);
// Load the shader source
gl.shaderSource(shader, shaderSource);
// Compile the shader
gl.compileShader(shader);
// Check the compile status
const compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
// Something went wrong during compilation; get the error
const lastError = gl.getShaderInfoLog(shader);
errFn('*** Error compiling shader "' + shader + '":' + lastError);
gl.deleteShader(shader);
return null;
}
return shader;
}
function error(msg) {
if (window.console) {
if (window.console.error) {
window.console.error(msg);
} else if (window.console.log) {
window.console.log(msg);
}
}
}
function postMessage(message){
window.webkit.messageHandlers.browserDelegate.postMessage(message);
}
</script>
</head>
<body>
<canvas id="gl"></canvas>
<div id="console"></div>
</body>
</html>
'''.replace("__IMAGE__", image_path)
with open(index_path, "w") as f:
f.write(content)
class BrowserDelegate(AppKit.NSObject):
def userContentController_didReceiveScriptMessage_(self, controller, message):
action = message.body()
if action == "loaded":
hide_cursor()
window.makeKeyAndOrderFront_(window)
currentApp.activateWithOptions_(AppKit.NSApplicationActivateIgnoringOtherApps)
window.setAlphaValue_(1.0)
window.makeFirstResponder_(webkit)
elif action == "exit":
show_cursor()
shutil.rmtree(tmp_dir, ignore_errors=True)
AppHelper.stopEventLoop()
browserDelegate = BrowserDelegate.alloc().init().retain()
config.userContentController().addScriptMessageHandler_name_(browserDelegate, 'browserDelegate')
webkit.loadFileURL_allowingReadAccessToURL_(index_url, base_url)
window.setContentView_(webkit)
window.makeFirstResponder_(webkit)
AppHelper.runEventLoop()
@nikolay-n
Copy link
Author

To launch:
img=/tmp/screen.png; screencapture "$img"; python2.7 ./glitch.py "$img"; unlink "$img"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment