Skip to content

Commit 727d9da

Browse files
author
monkstone
committed
fake floyd steinberg dithering
1 parent 07f2457 commit 727d9da

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Original shader by RavenWorks
2+
// Fake Floyd-Steinberg dithering
3+
// https://www.shadertoy.com/view/4sjGRD
4+
5+
// Adapted for Ruby-Processing by Martin Prout <@monkstoneT>
6+
7+
#ifdef GL_ES
8+
precision mediump float;
9+
precision mediump int;
10+
#endif
11+
12+
uniform sampler2D texture; // iChannel0 in Shadertoy
13+
uniform vec2 sketchSize; // iResolution in Shadertoy
14+
15+
const int lookupSize = 64;
16+
const float errorCarry = 0.3;
17+
18+
float getGrayscale(vec2 coords){
19+
vec2 uv = coords / sketchSize.xy;
20+
// processing is already using inverted y coordinates
21+
// uv.y = 1.0-uv.y;
22+
vec3 sourcePixel = texture2D(texture, uv).rgb;
23+
return length(sourcePixel*vec3(0.2126,0.7152,0.0722));
24+
}
25+
26+
// in regular glsl was
27+
// void mainImage( out vec4 fragColor, in vec2 fragCoord )
28+
29+
void main() {
30+
31+
int topGapY = int(sketchSize.y - gl_FragCoord.y);
32+
33+
int cornerGapX = int((gl_FragCoord.x < 10.0) ? gl_FragCoord.x : sketchSize.x - gl_FragCoord.x);
34+
int cornerGapY = int((gl_FragCoord.y < 10.0) ? sketchSize.y : gl_FragCoord.y - gl_FragCoord.y);
35+
int cornerThreshhold = ((cornerGapX == 0) || (topGapY == 0)) ? 5 : 4;
36+
37+
if (cornerGapX+cornerGapY < cornerThreshhold) {
38+
39+
gl_FragColor = vec4(0,0,0,1);
40+
41+
} else if (topGapY < 20) {
42+
43+
if (topGapY == 19) {
44+
45+
gl_FragColor = vec4(0,0,0,1);
46+
47+
} else {
48+
49+
gl_FragColor = vec4(1,1,1,1);
50+
51+
}
52+
53+
} else {
54+
55+
float xError = 0.0;
56+
for(int xLook=0; xLook<lookupSize; xLook++){
57+
float grayscale = getGrayscale(gl_FragCoord.xy + vec2(-lookupSize+xLook,0));
58+
grayscale += xError;
59+
float bit = grayscale >= 0.5 ? 1.0 : 0.0;
60+
xError = (grayscale - bit)*errorCarry;
61+
}
62+
63+
float yError = 0.0;
64+
for(int yLook=0; yLook<lookupSize; yLook++){
65+
float grayscale = getGrayscale(gl_FragCoord.xy + vec2(0,-lookupSize+yLook));
66+
grayscale += yError;
67+
float bit = grayscale >= 0.5 ? 1.0 : 0.0;
68+
yError = (grayscale - bit)*errorCarry;
69+
}
70+
71+
float finalGrayscale = getGrayscale(gl_FragCoord.xy);
72+
finalGrayscale += xError*0.5 + yError*0.5;
73+
float finalBit = finalGrayscale >= 0.5 ? 1.0 : 0.0;
74+
75+
gl_FragColor = vec4(finalBit,finalBit,finalBit,1);
76+
77+
}
78+
79+
}

video_filtering/steinberg.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Because this sketch uses a glsl shader it needs to run using
2+
# jruby-complete (typically rp5 --nojruby sketch.rb)
3+
# hold down mouse to see unfiltered output
4+
load_libraries :video, :video_event
5+
include_package 'processing.video'
6+
attr_reader :cam, :my_shader
7+
8+
def setup
9+
size(640, 480, P2D)
10+
@my_shader = load_shader('steinberg.glsl')
11+
my_shader.set('sketchSize', width.to_f, height.to_f)
12+
start_capture(width, height)
13+
end
14+
15+
def start_capture(w, h)
16+
@cam = Capture.new(self, w, h)
17+
cam.start
18+
end
19+
20+
def draw
21+
image(cam, 0, 0)
22+
return if mouse_pressed?
23+
filter(my_shader)
24+
end
25+
26+
# using snake case to match java reflect method
27+
def captureEvent(c)
28+
c.read
29+
end

0 commit comments

Comments
 (0)