Skip to content

Instantly share code, notes, and snippets.

@demotomohiro
Last active June 30, 2022 09:40
Show Gist options
  • Save demotomohiro/6ae8baf1e40fffae3858c2ff01a8d241 to your computer and use it in GitHub Desktop.
Save demotomohiro/6ae8baf1e40fffae3858c2ff01a8d241 to your computer and use it in GitHub Desktop.
Realtime pixel drawing
import std/[strformat, strutils, bitops]
import pixie, chroma
# define `nort` when opengl or windows system is not available.
when not defined(nort):
import nimgl/[glfw, opengl]
proc `{}`(image: var Image, x, y: int): var ColorRGBX =
## Accessing any pixels outside the bounds of the image is error
when not defined(danger):
if not image.inside(x, y):
raise newException(IndexDefect, &"({x}, {y}) not in range ({image.width - 1}, {image.height - 1})")
image.unsafe[x, y]
proc `{}=`(image: var Image, x, y: int; c: ColorRGBX) =
`{}`(image, x, y) = c
template drawToFile(img: Image; numFrames: int; filename: string; body: untyped): untyped =
for i in 0 .. numFrames:
body
img.writeFile(filename % ["frameNo", intToStr(i, 5)])
# These variable can be changed with '0'~'9' keys on keyboard.
# You can use them to change values in your code quickly with keyboard.
# Push shift key to change tuneKey2
var tuneKey, tuneKey2 = 0
when not defined(nort):
proc keyProc(window: GLFWWindow, key: int32, scancode: int32,
action: int32, mods: int32): void {.cdecl.} =
if key == GLFWKey.ESCAPE and action == GLFWPress:
window.setWindowShouldClose(true)
if action == GLFWPress and key in GLFWKey.K0 .. GLFWKey.K9:
let n = key - GLFWKey.K0
if (mods and GLFWModShift) == 1:
tuneKey2 = n
else:
tuneKey = n
template realtimeDraw(img: Image; body: untyped): untyped =
assert glfwInit()
glfwWindowHint(GLFWResizable, GLFW_FALSE)
let w: GLFWWindow = glfwCreateWindow(img.width.int32, img.height.int32,
"Window Title")
if w == nil:
quit(-1)
discard w.setKeyCallback(keyProc)
w.makeContextCurrent()
assert glInit()
glRasterPos2f(-1.0, 1.0)
glPixelZoom(1.0, -1.0)
echo "Entering main loop"
while not w.windowShouldClose:
glfwPollEvents()
body
glDrawPixels(img.width.GLsizei, img.height.GLsizei,
GL_RGBA, GL_UNSIGNED_BYTE,
img.data[0].addr)
w.swapBuffers()
w.destroyWindow()
glfwTerminate()
proc main =
var
image = newImage(512, 512)
frameN = 0
frameN2 = 0
realtimeDraw(image):
#drawToFile(image, 1024, "testout$frameNo.png"):
for y in 0 ..< image.height:
for x in 0 ..< image.width:
var
c: uint8
speed = frameN
# https://oeis.org/A007597
for i in 0..3:
let
x1 = uint32 x + speed
y1 = uint32 y + speed
a = ((x1 shr 8) * 1861111981) xor ((y1 shr 8) * 1981111861)
b = (a * 1981111861).reverseBits # Pseudo random number from pixel coordinate
d = (b and 1) - 1
e = uint8 (x1 xor y1 xor d) + frameN2.uint32
#c = uint8 b
if (e and (128 shr i).uint8) > 0:
c = e and (not 0u8 shr i)
break
speed = speed * 3 div 4
image{x, y} = rgbx(c, c, c, 255)
inc frameN
if (frameN and 64) > 0:
inc frameN2
main()
@demotomohiro
Copy link
Author

Output preview:
https://user-images.githubusercontent.com/1882512/174388144-c1928749-9903-479b-abfb-22fe508290a6.mp4
Generated with

$ ffmpeg -f image2 -framerate 60 -i testout%05d.png testout.mp4

(You need to comment out realtimeDraw(image): and enable drawToFile(image, 1024, "testout$frameNo.png"): in main proc)

This code uses following great libraries:
https://github.com/treeform/pixie
https://github.com/nimgl/nimgl

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