Skip to content

Instantly share code, notes, and snippets.

@NickBeeuwsaert
Created February 15, 2016 02:47
Show Gist options
  • Save NickBeeuwsaert/29780f01debf60dedc8a to your computer and use it in GitHub Desktop.
Save NickBeeuwsaert/29780f01debf60dedc8a to your computer and use it in GitHub Desktop.
Using SDL2 and Cairo from python
"""I'm using ctypes to interact with cairo instead of pycairo
because pycairo hasn't been updated in 4 years and has features missing
in python3.
"""
import ctypes
import ctypes.util
from ctypes import c_void_p, c_double
# Enumerate pixel formats
FORMAT_INVALID = -1
FORMAT_ARGB32 = 0
FORMAT_RGB24 = 1
FORMAT_A8 = 2
FORMAT_A1 = 3
FORMAT_RGB16_565 = 4
FORMAT_RGB30 = 5
# Enumeration of
OPERATOR_CLEAR = 0
OPERATOR_SOURCE = 1
OPERATOR_OVER = 2
OPERATOR_IN = 3
OPERATOR_OUT = 4
OPERATOR_ATOP = 5
OPERATOR_DEST = 6
OPERATOR_DEST_OVER = 7
OPERATOR_DEST_IN = 8
OPERATOR_DEST_OUT = 9
OPERATOR_DEST_ATOP = 10
OPERATOR_XOR = 11
OPERATOR_ADD = 12
OPERATOR_SATURATE = 13
OPERATOR_MULTIPLY = 14
OPERATOR_SCREEN = 15
OPERATOR_OVERLAY = 16
OPERATOR_DARKEN = 17
OPERATOR_LIGHTEN = 18
OPERATOR_COLOR_DODGE = 19
OPERATOR_COLOR_BURN = 20
OPERATOR_HARD_LIGHT = 21
OPERATOR_SOFT_LIGHT = 22
OPERATOR_DIFFERENCE = 23
OPERATOR_EXCLUSION = 24
OPERATOR_HSL_HUE = 25
OPERATOR_HSL_SATURATION = 26
OPERATOR_HSL_COLOR = 27
OPERATOR_HSL_LUMINOSITY = 28
cairo = ctypes.CDLL(ctypes.util.find_library("cairo"))
image_surface_create = cairo.cairo_image_surface_create
image_surface_create.restype = ctypes.POINTER(c_void_p)
image_surface_get_data = cairo.cairo_image_surface_get_data
image_surface_get_data.restype = ctypes.POINTER(c_void_p)
surface_destroy = cairo.cairo_surface_destroy
create = cairo.cairo_create
create.restype = ctypes.POINTER(c_void_p)
destroy = cairo.cairo_destroy
set_source_rgba = cairo.cairo_set_source_rgba
set_source_rgba.argtypes = [c_void_p, c_double, c_double, c_double, c_double]
set_source_rgb = cairo.cairo_set_source_rgb
set_source_rgb.argtypes = [c_void_p, c_double, c_double, c_double]
set_operator = cairo.cairo_set_operator
set_line_width = cairo.cairo_set_line_width
set_line_width.argtypes = [c_void_p, c_double]
stroke = cairo.cairo_stroke
fill = cairo.cairo_fill
paint = cairo.cairo_paint
move_to = cairo.cairo_move_to
move_to.argtypes = [c_void_p, c_double, c_double]
line_to = cairo.cairo_line_to
line_to.argtypes = [c_void_p, c_double, c_double]
curve_to = cairo.cairo_curve_to
curve_to.argtypes = [c_void_p, c_double, c_double, c_double, c_double, c_double, c_double]
close_path = cairo.cairo_close_path
#!/usr/bin/env python
from __future__ import division
import sdl
import time
import ctypes
import ccairo
from collections import deque
import colorsys
window, renderer = sdl.CreateWindowAndRenderer(800, 600, sdl.WINDOW_OPENGL)
# sdl.SetRenderDrawBlendMode(renderer, sdl.BLENDMODE_BLEND)
texture = sdl.CreateTexture(renderer, sdl.PIXELFORMAT_ARGB8888, sdl.TEXTUREACCESS_STREAMING, 800, 600)
sdl.SetTextureBlendMode(texture, sdl.BLENDMODE_BLEND)
cairo_surface = ccairo.image_surface_create(ccairo.FORMAT_ARGB32, 800, 600)
ctx = ccairo.create(cairo_surface)
data = ccairo.image_surface_get_data(cairo_surface)
running = True
hue = 0
dirty = True
mouse_trail = deque(maxlen=100)
while running:
hue = (hue + 0.5) % 360
r, g, b = map(lambda c: int(c * 255), colorsys.hls_to_rgb(hue / 360, 0.5, 0.5))
sdl.SetRenderDrawColor(renderer, r, g, b)
sdl.RenderClear(renderer)
# Avoid needless expensive operations
if dirty:
# Clear the cairo surface
ccairo.set_source_rgba(ctx, 1, 1, 1, 0)
ccairo.set_operator(ctx, ccairo.OPERATOR_SOURCE)
ccairo.paint(ctx)
ccairo.set_operator(ctx, ccairo.OPERATOR_OVER)
ccairo.set_source_rgba(ctx, 0.0, 0.0, 0.0, 1.0)
for i, (x, y) in enumerate(mouse_trail):
if i == 0:
ccairo.move_to(ctx, x, y)
else:
ccairo.line_to(ctx, x, y)
ccairo.stroke(ctx)
# Update the texture with the contents of the cairo surface
with sdl.LockTexture(texture) as (pixels, pitch):
ctypes.memmove(pixels, data, 800 * 600 * 4)
dirty = False
sdl.RenderCopy(renderer, texture, None, None)
sdl.RenderPresent(renderer)
for event in sdl.PollEvent():
if event.type == sdl.QUIT:
running = False
elif event.type == sdl.MOUSEMOTION:
mouse_trail.append((event.motion.x, event.motion.y))
dirty = True
# print(event.motion.x, event.motion.y)
ccairo.destroy(ctx)
ccairo.surface_destroy(cairo_surface)
sdl.DestroyTexture(texture)
sdl.DestroyRenderer(renderer)
sdl.DestroyWindow(window)
"""I'm not using pygame because it doesn't play very nice with cairo"""
import ctypes
import ctypes.util
from ctypes import c_void_p
import contextlib
SDL = ctypes.CDLL(ctypes.util.find_library("sdl2"))
WINDOW_FULLSCREEN = 1 << 0
WINDOW_OPENGL = 1 << 1
WINDOW_SHOWN = 1 << 2
WINDOW_HIDDEN = 1 << 3
WINDOW_BORDERLESS = 1 << 4
WINDOW_RESIZABLE = 1 << 5
WINDOW_MINIMIZED = 1 << 6
WINDOW_MAXIMIZED = 1 << 7
WINDOW_INPUT_GRABBED = 1 << 8
WINDOW_INPUT_FOCUS = 1 << 9
WINDOW_MOUSE_FOCUS = 1 << 10
WINDOW_FULLSCREEN_DESKTOP = WINDOW_FULLSCREEN | 1 << 12
WINDOW_FOREIGN = 1 << 11
WINDOW_ALLOW_HIGHDPI = 1 << 13
WINDOW_MOUSE_CAPTURE = 1 << 14
WINDOW_ALWAYS_ON_TOP = 1 << 15
WINDOW_SKIP_TASKBAR = 1 << 16
WINDOW_UTILITY = 1 << 17
WINDOW_TOOLTIP = 1 << 18
WINDOW_POPUP_MENU = 1 << 19
BLENDMODE_NONE = 0
BLENDMODE_BLEND = 1
BLENDMODE_ADD = 2
BLENDMODE_MOD = 3
QUIT = 0x100
MOUSEMOTION = 0x400
MOUSEBUTTONDOWN = destroyMOUSEBUTTONUP = 0x402
CreateWindow = SDL.SDL_CreateWindow
CreateWindow.restype = ctypes.POINTER(c_void_p)
CreateRenderer = SDL.SDL_CreateRenderer
CreateRenderer.restype = ctypes.POINTER(c_void_p)
SetRenderDrawBlendMode = SDL.SDL_SetRenderDrawBlendMode
SetTextureBlendMode = SDL.SDL_SetTextureBlendMode
PIXELTYPE_UNKNOWN = 0
PIXELTYPE_INDEX1 = 1
PIXELTYPE_INDEX4 = 2
PIXELTYPE_INDEX8 = 3
PIXELTYPE_PACKED8 = 4
PIXELTYPE_PACKED16 = 5
PIXELTYPE_PACKED32 = 6
PIXELTYPE_ARRAYU8 = 7
PIXELTYPE_ARRAYU16 = 8
PIXELTYPE_ARRAYU32 = 9
PIXELTYPE_ARRAYF16 = 10
PIXELTYPE_ARRAYF32 = 11
PACKEDORDER_NONE = 0
PACKEDORDER_XRGB = 1
PACKEDORDER_RGBX = 2
PACKEDORDER_ARGB = 3
PACKEDORDER_RGBA = 4
PACKEDORDER_XBGR = 5
PACKEDORDER_BGRX = 6
PACKEDORDER_ABGR = 7
PACKEDORDER_BGRA = 8
PACKEDLAYOUT_NONE = 0
PACKEDLAYOUT_332 = 1
PACKEDLAYOUT_4444 = 2
PACKEDLAYOUT_1555 = 3
PACKEDLAYOUT_5551 = 4
PACKEDLAYOUT_565 = 5
PACKEDLAYOUT_8888 = 6
PACKEDLAYOUT_2101010 = 7
PACKEDLAYOUT_1010102 = 8
def _DEFINE_PIXELFORMAT(type, order, layout, bits, bytes):
return ((1 << 28) | ((type) << 24) | ((order) << 20) | ((layout) << 16) | \
((bits) << 8) | ((bytes) << 0))
PIXELFORMAT_ARGB8888 = _DEFINE_PIXELFORMAT(PIXELTYPE_PACKED32, PACKEDORDER_ARGB, PACKEDLAYOUT_8888, 32, 4)
TEXTUREACCESS_STATIC = 0
TEXTUREACCESS_STREAMING = 1
TEXTUREACCESS_TARGET = 2
CreateTexture = SDL.SDL_CreateTexture
CreateTexture.restype = ctypes.POINTER(c_void_p)
DestroyTexture = SDL.SDL_DestroyTexture
_LockTexture = SDL.SDL_LockTexture
_UnlockTexture = SDL.SDL_UnlockTexture
@contextlib.contextmanager
def LockTexture(texture, rect=None):
pixels = c_void_p()
pitch = ctypes.c_int()
_LockTexture(texture, rect, ctypes.byref(pixels), ctypes.byref(pitch))
yield pixels, pitch
_UnlockTexture(texture)
def CreateWindowAndRenderer(width, height, flags):
window, renderer = c_void_p(), c_void_p()
if SDL.SDL_CreateWindowAndRenderer(width, height, flags, ctypes.byref(window), ctypes.byref(renderer)) != 0:
raise Exception("Trouble during create window and renderer")
return window, renderer
DestroyRenderer = SDL.SDL_DestroyRenderer
DestroyWindow = SDL.SDL_DestroyWindow
RenderClear = SDL.SDL_RenderClear
RenderCopy = SDL.SDL_RenderCopy
RenderPresent = SDL.SDL_RenderPresent
SetRenderDrawColor = SDL.SDL_SetRenderDrawColor
class QuitEvent(ctypes.Structure):
_fields_ = [
("type", ctypes.c_uint32),
("timestamp", ctypes.c_uint32)
]
class MouseMotionEvent(ctypes.Structure):
_fields_ = [
("type", ctypes.c_uint32),
("timestamp", ctypes.c_uint32),
("window_id", ctypes.c_uint32),
("which", ctypes.c_uint32),
("state", ctypes.c_uint32),
("x", ctypes.c_int32),
("y", ctypes.c_int32),
("xrel", ctypes.c_int32),
("yrel", ctypes.c_int32),
]
class MouseButtonEvent(ctypes.Structure):
_fields_ = [
("type", ctypes.c_uint32),
("timestamp", ctypes.c_uint32),
("window_id", ctypes.c_uint32),
("button", ctypes.c_uint8),
("state", ctypes.c_uint8),
("clicks", ctypes.c_uint8),
("padding1", ctypes.c_uint8),
("x", ctypes.c_int32),
("y", ctypes.c_int32),
]
class Event(ctypes.Union):
_fields_ = [
("type", ctypes.c_uint32),
("motion", MouseMotionEvent),
("button", MouseButtonEvent),
("padding", ctypes.c_byte * 56)
]
def PollEvent():
event = Event()
while SDL.SDL_PollEvent(ctypes.byref(event)):
yield event
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment