Created
February 15, 2016 02:47
-
-
Save NickBeeuwsaert/29780f01debf60dedc8a to your computer and use it in GitHub Desktop.
Using SDL2 and Cairo from python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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