diff --git a/README.md b/README.md index 23e4627..e5edc0a 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,14 @@ Q,q,T,t | ✅ | quadratic to | A,a | ✅ | arc to | z | ✅ | close path | +### Realtime Examples + +Here are some examples of using Pixie for realtime rendering with some popular windowing libraries: + +* [examples/realtime_glfw.nim](examples/realtime_glfw.nim) +* [examples/realtime_glut.nim](examples/realtime_glut.nim) +* [examples/realtime_sdl.nim](examples/realtime_sdl.nim) +* [examples/realtime_win32.nim](examples/realtime_win32.nim) ## Testing diff --git a/examples/realtime_glfw.nim b/examples/realtime_glfw.nim new file mode 100644 index 0000000..4daf53a --- /dev/null +++ b/examples/realtime_glfw.nim @@ -0,0 +1,78 @@ +## This example show how to have real time pixie using glfw API. + +import math, opengl, pixie, staticglfw + +let + w: int32 = 256 + h: int32 = 256 + +var + screen = newImage(w, h) + ctx = newContext(screen) + frameCount = 0 + window: Window + +proc display() = + ## Called every frame by main while loop + + # draw shiny sphere on gradient background + var linerGradient = Paint(kind: pkGradientLinear) + linerGradient.gradientHandlePositions.add(vec2(0, 0)) + linerGradient.gradientHandlePositions.add(vec2(0, 256)) + linerGradient.gradientStops.add( + ColorStop(color: rgbx(0, 0, 0, 255), position: 0)) + linerGradient.gradientStops.add( + ColorStop(color: rgbx(255, 255, 255, 255), position: 1)) + ctx.fillStyle = linerGradient + ctx.fillRect(0, 0, 256, 256) + + var radialGradient = Paint(kind: pkGradientRadial) + radialGradient.gradientHandlePositions.add(vec2(128, 128)) + radialGradient.gradientHandlePositions.add(vec2(256, 128)) + radialGradient.gradientHandlePositions.add(vec2(128, 256)) + radialGradient.gradientStops.add( + ColorStop(color: rgbx(255, 255, 255, 255), position: 0)) + radialGradient.gradientStops.add( + ColorStop(color: rgbx(0, 0, 0, 255), position: 1)) + ctx.fillStyle = radialGradient + ctx.fillCircle(vec2(128.0, 128.0 + sin(float(frameCount)/10.0) * 20), 76.8) + + # update texture with new pixels from surface + var dataPtr = ctx.image.data[0].addr + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GLsizei w, GLsizei h, GL_RGBA, + GL_UNSIGNED_BYTE, dataPtr) + + # draw a quad over the whole screen + glClear(GL_COLOR_BUFFER_BIT) + glBegin(GL_QUADS) + glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, +1.0) + glTexCoord2d(1.0, 0.0); glVertex2d(+1.0, +1.0) + glTexCoord2d(1.0, 1.0); glVertex2d(+1.0, -1.0) + glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, -1.0) + glEnd() + + inc frameCount + swapBuffers(window) + +if init() == 0: + quit("Failed to Initialize GLFW.") + +windowHint(RESIZABLE, false.cint) +window = createWindow(w.cint, h.cint, "GLFW/Pixie", nil, nil) + +makeContextCurrent(window) +loadExtensions() + +# allocate a texture and bind it +var dataPtr = ctx.image.data[0].addr +glTexImage2D(GL_TEXTURE_2D, 0, 3, GLsizei w, GLsizei h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, dataPtr) +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP) +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP) +glEnable(GL_TEXTURE_2D) + +while windowShouldClose(window) != 1: + pollEvents() + display() diff --git a/examples/realtime_glut.nim b/examples/realtime_glut.nim new file mode 100644 index 0000000..acf49e8 --- /dev/null +++ b/examples/realtime_glut.nim @@ -0,0 +1,76 @@ +## This example show how to have real time pixie using glut API. + +import math, opengl, opengl/glu, opengl/glut, pixie + +let + w: int32 = 256 + h: int32 = 256 + +var + screen = newImage(w, h) + ctx = newContext(screen) + frameCount = 0 + +proc display() {.cdecl.} = + ## Called every frame by GLUT + + # draw shiny sphere on gradient background + var linerGradient = Paint(kind: pkGradientLinear) + linerGradient.gradientHandlePositions.add(vec2(0, 0)) + linerGradient.gradientHandlePositions.add(vec2(0, 256)) + linerGradient.gradientStops.add( + ColorStop(color: rgbx(0, 0, 0, 255), position: 0)) + linerGradient.gradientStops.add( + ColorStop(color: rgbx(255, 255, 255, 255), position: 1)) + ctx.fillStyle = linerGradient + ctx.fillRect(0, 0, 256, 256) + + var radialGradient = Paint(kind: pkGradientRadial) + radialGradient.gradientHandlePositions.add(vec2(128, 128)) + radialGradient.gradientHandlePositions.add(vec2(256, 128)) + radialGradient.gradientHandlePositions.add(vec2(128, 256)) + radialGradient.gradientStops.add( + ColorStop(color: rgbx(255, 255, 255, 255), position: 0)) + radialGradient.gradientStops.add( + ColorStop(color: rgbx(0, 0, 0, 255), position: 1)) + ctx.fillStyle = radialGradient + ctx.fillCircle(vec2(128.0, 128.0 + sin(float(frameCount)/10.0) * 20), 76.8) + + # update texture with new pixels from surface + var dataPtr = ctx.image.data[0].addr + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GLsizei w, GLsizei h, GL_RGBA, + GL_UNSIGNED_BYTE, dataPtr) + + # draw a quad over the whole screen + glClear(GL_COLOR_BUFFER_BIT) + glBegin(GL_QUADS) + glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, +1.0) + glTexCoord2d(1.0, 0.0); glVertex2d(+1.0, +1.0) + glTexCoord2d(1.0, 1.0); glVertex2d(+1.0, -1.0) + glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, -1.0) + glEnd() + glutSwapBuffers() + + inc frameCount + + glutPostRedisplay() # ask glut to draw next frame + +glutInit() +glutInitDisplayMode(GLUT_DOUBLE) +glutInitWindowSize(w, h) +discard glutCreateWindow("GLUT/Pixie") + +glutDisplayFunc(display) +loadExtensions() + +# allocate a texture and bind it +var dataPtr = ctx.image.data[0].addr +glTexImage2D(GL_TEXTURE_2D, 0, 3, GLsizei w, GLsizei h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, dataPtr) +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP) +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP) +glEnable(GL_TEXTURE_2D) + +glutMainLoop() diff --git a/examples/realtime_sdl.nim b/examples/realtime_sdl.nim new file mode 100644 index 0000000..a93cfce --- /dev/null +++ b/examples/realtime_sdl.nim @@ -0,0 +1,68 @@ +## This example show how to have real time pixie using sdl2 API. + +import math, pixie, sdl2, sdl2/gfx + +let + w: int32 = 256 + h: int32 = 256 + +var + screen = newImage(w, h) + ctx = newContext(screen) + frameCount = 0 + window: WindowPtr + render: RendererPtr + mainSerface: SurfacePtr + mainTexture: TexturePtr + evt = sdl2.defaultEvent + +proc display() = + ## Called every frame by main while loop + + # draw shiny sphere on gradient background + var linerGradient = Paint(kind: pkGradientLinear) + linerGradient.gradientHandlePositions.add(vec2(0, 0)) + linerGradient.gradientHandlePositions.add(vec2(0, 256)) + linerGradient.gradientStops.add( + ColorStop(color: rgbx(0, 0, 0, 255), position: 0)) + linerGradient.gradientStops.add( + ColorStop(color: rgbx(255, 255, 255, 255), position: 1)) + ctx.fillStyle = linerGradient + ctx.fillRect(0, 0, 256, 256) + + var radialGradient = Paint(kind: pkGradientRadial) + radialGradient.gradientHandlePositions.add(vec2(128, 128)) + radialGradient.gradientHandlePositions.add(vec2(256, 128)) + radialGradient.gradientHandlePositions.add(vec2(128, 256)) + radialGradient.gradientStops.add( + ColorStop(color: rgbx(255, 255, 255, 255), position: 0)) + radialGradient.gradientStops.add( + ColorStop(color: rgbx(0, 0, 0, 255), position: 1)) + ctx.fillStyle = radialGradient + ctx.fillCircle(vec2(128.0, 128.0 + sin(float(frameCount)/10.0) * 20), 76.8) + + inc frameCount + + var dataPtr = ctx.image.data[0].addr + mainSerface.pixels = dataPtr + mainTexture = render.createTextureFromSurface(mainSerface) + render.copy(mainTexture, nil, nil) + render.present() + +discard sdl2.init(INIT_EVERYTHING) +window = createWindow("SDL/Pixie", 100, 100, cint w, cint h, SDL_WINDOW_SHOWN) +const + rmask = uint32 0x000000ff + gmask = uint32 0x0000ff00 + bmask = uint32 0x00ff0000 + amask = uint32 0xff000000 +mainSerface = createRGBSurface(0, cint w, cint h, 32, rmask, gmask, bmask, amask) + +render = createRenderer(window, -1, 0) + +while true: + while pollEvent(evt): + if evt.kind == QuitEvent: + quit(0) + display() + delay(14) diff --git a/examples/realtime_win32.nim b/examples/realtime_win32.nim new file mode 100644 index 0000000..d4c2d9c --- /dev/null +++ b/examples/realtime_win32.nim @@ -0,0 +1,131 @@ +## This example show how to have real time pixie using win32 API. + +import pixie, winim/lean + +let + w: int32 = 256 + h: int32 = 256 + +var + screen = newImage(w, h) + ctx = newContext(screen) + frameCount = 0 + hwnd: HWND + running = true + +proc draw() = + # draw shiny sphere on gradient background + var linerGradient = Paint(kind: pkGradientLinear) + linerGradient.gradientHandlePositions.add(vec2(0, 0)) + linerGradient.gradientHandlePositions.add(vec2(0, 256)) + linerGradient.gradientStops.add( + ColorStop(color: rgbx(0, 0, 0, 255), position: 0)) + linerGradient.gradientStops.add( + ColorStop(color: rgbx(255, 255, 255, 255), position: 1)) + ctx.fillStyle = linerGradient + ctx.fillRect(0, 0, 256, 256) + + var radialGradient = Paint(kind: pkGradientRadial) + radialGradient.gradientHandlePositions.add(vec2(128, 128)) + radialGradient.gradientHandlePositions.add(vec2(256, 128)) + radialGradient.gradientHandlePositions.add(vec2(128, 256)) + radialGradient.gradientStops.add( + ColorStop(color: rgbx(255, 255, 255, 255), position: 0)) + radialGradient.gradientStops.add( + ColorStop(color: rgbx(0, 0, 0, 255), position: 1)) + ctx.fillStyle = radialGradient + ctx.fillCircle(vec2(128.0, 128.0 + sin(float(frameCount)/10.0) * 20), 76.8) + + inc frameCount + + # Draw image pixels onto win32 window. + let + w = screen.width.int32 + h = screen.height.int32 + dc = GetDC(hwnd) + var info = BITMAPINFO() + info.bmiHeader.biBitCount = 32 + info.bmiHeader.biWidth = w + info.bmiHeader.biHeight = h + info.bmiHeader.biPlanes = 1 + info.bmiHeader.biSize = DWORD sizeof(BITMAPINFOHEADER) + info.bmiHeader.biSizeImage = w * h * 4 + info.bmiHeader.biCompression = BI_RGB + var bgrBuffer = newSeq[uint8](screen.data.len * 4) + # Convert to BGRA. + for i, c in screen.data: + bgrBuffer[i*4+0] = c.b + bgrBuffer[i*4+1] = c.g + bgrBuffer[i*4+2] = c.r + discard StretchDIBits( + dc, + 0, + h - 1, + w, + -h, + 0, + 0, + w, + h, + bgrBuffer[0].addr, + info, + DIB_RGB_COLORS, + SRCCOPY + ) + discard ReleaseDC(hwnd, dc) + +proc windowProc(hwnd: HWND, message: UINT, wParam: WPARAM, + lParam: LPARAM): LRESULT {.stdcall.} = + case message + of WM_DESTROY: + PostQuitMessage(0) + running = false + return 0 + else: + return DefWindowProc(hwnd, message, wParam, lParam) + +proc main() = + var + hInstance = GetModuleHandle(nil) + appName = "Win32/Pixie" + msg: MSG + wndclass: WNDCLASS + + wndclass.style = CS_HREDRAW or CS_VREDRAW + wndclass.lpfnWndProc = windowProc + wndclass.cbClsExtra = 0 + wndclass.cbWndExtra = 0 + wndclass.hInstance = hInstance + wndclass.hIcon = LoadIcon(0, IDI_APPLICATION) + wndclass.hCursor = LoadCursor(0, IDC_ARROW) + wndclass.hbrBackground = GetStockObject(WHITE_BRUSH) + wndclass.lpszMenuName = nil + wndclass.lpszClassName = appName + + if RegisterClass(wndclass) == 0: + MessageBox(0, "This program requires Windows NT!", appName, MB_ICONERROR) + return + + # Figure out the right size of the window we want. + var rect: lean.RECT + rect.left = 0 + rect.top = 0 + rect.right = w + rect.bottom = h + AdjustWindowRectEx(cast[LPRECT](rect.addr), WS_OVERLAPPEDWINDOW, 0, 0) + + # Open the window. + hwnd = CreateWindow(appName, "Win32/Pixie", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, + 0, 0, hInstance, nil) + + ShowWindow(hwnd, SW_SHOW) + UpdateWindow(hwnd) + + while running: + draw() + PeekMessage(msg, 0, 0, 0, PM_REMOVE) + TranslateMessage(msg) + DispatchMessage(msg) + +main()