def add_light(self, name, width, height): # set on first call only if 0 == self.light_size: self.light_size = width if width != height: raise SDLError('width and height of light image must be equal') # check for different size if width != self.light_size: raise SDLError('all light images must have the same resolution') self.lights[name] = width
def aapolygon(surface, points, color, sdlgfx=None): """ Draw anti-aliased polygon with alpha blending. Minimum number of points (vertices coordinates) is 3. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. points (list of n 2-tuple int): list of X and Y tuples with vertices coordinates for the aa-polygon. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) lx, ly = zip(*points) vx = (ctypes.c_short * len(lx))(*lx) vy = (ctypes.c_short * len(ly))(*ly) n = len(vy) if sdlgfx.aapolygonRGBA(surface, vx, vy, n, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.aapolygonRGBA failure")
def rectangle(surface, rect, color, sdlgfx=None): """Draw rectangle with blending. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. x1 (int): X coordinate of the first point (i.e. top right) of the rectangle. y1 (int): Y coordinate of the first point (i.e. top right) of the rectangle. x2 (int): X coordinate of the second point (i.e. bottom left) of the rectangle. y2 (int): Y coordinate of the second point (i.e. bottom left) of the rectangle. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if not isinstance(rect, Rect): rect = Rect(rect) if sdlgfx.rectangleRGBA(surface, rect.x, rect.y, rect.x + rect.w, rect.y + rect.h, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.rectangleRGBA failure")
def render(self, sprites, x=None, y=None): """Overrides the render method of sdl2.ext.TextureSpriteRenderSystem to use "SDL_RenderCopyEx" instead of "SDL_RenderCopy" to allow sprite rotation: http://wiki.libsdl.org/SDL_RenderCopyEx """ r = SDL_Rect(0, 0, 0, 0) if isiterable(sprites): rcopy = SDL_RenderCopyEx renderer = self.sdlrenderer x = x or 0 y = y or 0 for sp in sprites: r.x = x + sp.x r.y = y + sp.y r.w, r.h = sp.size if rcopy(renderer, sp.texture, None, r, sp.angle, None, SDL_FLIP_NONE) == -1: raise SDLError() else: r.x = sprites.x r.y = sprites.y r.w, r.h = sprites.size if x is not None and y is not None: r.x = x r.y = y SDL_RenderCopyEx(self.sdlrenderer, sprites.texture, None, r, sprites.angle, None, SDL_FLIP_NONE) SDL_RenderPresent(self.sdlrenderer)
def bezier(surface, points, steps, color, sdlgfx=None): """ Draw a bezier curve with alpha blending. Minimum number of points (vertices coordinates) is 3. Minimum number of steps for interpolation is 2. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. points (list of n 2-tuple int): list of X and Y tuples with points of the bezier curve. steps (int): Number of steps for the interpolation. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) lx, ly = zip(*points) vx = (ctypes.c_short * len(lx))(*lx) vy = (ctypes.c_short * len(ly))(*ly) n = len(vy) if sdlgfx.bezierRGBA(surface, vx, vy, n, steps, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.bezierRGBA failure")
def _create_texture(self): text_surface = TTF_RenderText_Shaded(self.font, self._text.encode("UTF-8"), self.text_color, self.background_color) if text_surface is None: raise TTF_GetError() texture = SDL_CreateTextureFromSurface(self.renderer, text_surface) if texture is None: raise SDLError() SDL_FreeSurface(text_surface) return texture
def pixel(surface, x, y, color, sdlgfx=None): """Draw pixel with blending enabled if alpha < 255. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. x (int): X (horizontal) coordinate of the pixel. y (int): Y (vertical) coordinate of the pixel. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if sdlgfx.pixelRGBA(surface, x, y, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.pixelRGBA failure")
def hline(surface, x1, x2, y, color, sdlgfx=None): """Draw horizontal line with blending. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. x1 (int): X coordinate of the first point (i.e. left) of the line. x2 (int): X coordinate of the second point (i.e. right) of the line. y (int): Y coordinate of the points of the line. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if sdlgfx.hlineRGBA(surface, x1, x2, y, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.hlineRGBA failure")
def filled_circle(surface, x, y, r, color, sdlgfx=None): """Draw filled circle with blending. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. x (int): X coordinate of the center of the filled circle. y (int): Y coordinate of the center of the filled circle. rad (int): Radius in pixels of the filled circle. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if sdlgfx.filledCircleRGBA(surface, x, y, r, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.filledCircleRGBA failure")
def aaellipse(surface, x, y, rx, ry, color, sdlgfx=None): """Draw anti-aliased ellipse with blending. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. x (int): X coordinate of the center of the aa-ellipse. y (int): Y coordinate of the center of the aa-ellipse. rx (int): Horizontal radius in pixels of the aa-ellipse. ry (int): Vertical radius in pixels of the aa-ellipse. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if sdlgfx.aaellipseRGBA(surface, x, y, rx, ry, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.aaellipseRGBA failure")
def aaline(surface, x1, y1, x2, y2, color, sdlgfx=None): """Draw anti-aliased line with alpha blending. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. x1 (int): X coordinate of the first point of the line. y1 (int): Y coordinate of the first point of the line. x2 (int): X coordinate of the second point of the line. y2 (int): Y coordinate of the second point of the line. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if sdlgfx.aalineRGBA(surface, x1, y1, x2, y2, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.aalineRGBA failure")
def box(surface, rect, color, sdlgfx=None): """Draw a filled rectangle with blending. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. rect (Rect): A rectangle representing the area to be draw. rad (int): The radius of the corner arc. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if not isinstance(rect, Rect): rect = Rect(rect) if sdlgfx.boxRGBA(surface, rect.x, rect.y, rect.x + rect.w, rect.y + rect.h, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.boxRGBA failure")
def pie(surface, x, y, r, start, end, color, sdlgfx=None): """Draw pie outline with alpha blending. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. x (int): X coordinate of the center of the pie. y (int): Y coordinate of the center of the pie. rad (int): Radius in pixels of the pie. start (int): Starting radius in degrees of the pie. end (int): Ending radius in degrees of the pie. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if sdlgfx.pieRGBA(surface, x, y, r, start, end, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.pieRGBA failure")
def rounded_rectangle(surface, rect, rad, color, sdlgfx=None): """Draw rounded-corner rectangle with blending. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. rect (Rect): A rectangle representing the area to be draw. rad (int): The radius of the corner arc. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if not isinstance(rect, Rect): rect = Rect(rect) if sdlgfx.roundedRectangleRGBA(surface, rect.right - 1, rect.top + 1, rect.left + 1, rect.bottom - 1, rad, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.rectangleRGBA failure")
def textured_polygon(surface, points, texture, texture_dx, texture_dy, sdlgfx=None): """ Draw a polygon filled with the given texture. Minimum number of points (vertices coordinates) is 3. If you move the polygon and want the texture to apear the same you need to change the texture_dx and/or texture_dy values. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. points (list of n 2-tuple int): list of X and Y tuples with vertices coordinates for the polygon. texture (SDL_Surface): the surface to use to fill the polygon texture_dx (int): the horizontal offset of the texture relative to the screeen. texture_dy (int): the vertical offset of the texture relative to the screeen. Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc lx, ly = zip(*points) vx = (ctypes.c_short * len(lx))(*lx) vy = (ctypes.c_short * len(ly))(*ly) n = len(vy) if sdlgfx.texturedPolygon(surface, vx, vy, n, texture, texture_dx, texture_dy): raise SDLError("sdlgfx.texturedPolygon failure")
def filled_trigon(surface, x1, y1, x2, y2, x3, y3, color, sdlgfx=None): """Draw filled trigon (triangle) with alpha blending. Note: Creates vertex array and uses aapolygon routine to render. Args: surface (SDL_RENDERER, SDL_TEXTURE): The renderer or texture (created with the SDL_TEXTUREACCESS_TARGET flag) to draw on. x1 (int): X coordinate of the first point of the aa-trigon. y1 (int): Y coordinate of the first point of the aa-trigon. x2 (int): X coordinate of the second point of the aa-trigon. y2 (int): Y coordinate of the second point of the aa-trigon. x3 (int): X coordinate of the third point of the aa-trigon. y3 (int): Y coordinate of the third point of the aa-trigon. color (Color or 4-tuple with ints between 0-255): RGBA color Raises: SDLError """ sdlgfx = sdlgfx_tex if (sdlgfx is None or sdlgfx == "tex") else sdlgfx_sfc c = Color(*color) if sdlgfx.filledTrigonRGBA(surface, x1, y1, x2, y2, x3, y3, c.r, c.g, c.b, c.a): raise SDLError("sdlgfx.filledTrigonRGBA failure")
def run(): # get monitors (used to get desktop resolution) monitors = get_monitors() if len(monitors) > 1: raise SDLError('only single screen supported') # init calculator (used for positions) calculator = Calculator() calculator.set_screen_size(monitors[0].width, monitors[0].height) # init main window sdl2.ext.init() window = sdl2.ext.Window('c2k', size=calculator.get_screen_size(), flags=sdl2.SDL_WINDOW_BORDERLESS) window.show() # init renderer (software) sprite_renderer = SoftwareRenderer(window) factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE) # init world an add renderer world = sdl2.ext.World() world.add_system(sprite_renderer) # dict of all lights as sprite in correct size light_sprites = {name: None for name in lights} for sprite_name in lights: # load surface and add resolution to calculator surface = sdl2.ext.load_image('resources/{}.png'.format(sprite_name)) calculator.add_light(sprite_name, surface.w, surface.h) # scale the surface light_sprites[sprite_name] = get_scaled_surface( surface, calculator.get_light_scale(sprite_name)) # calculate light positions calculator.calculate() # draw big grey lights for pos in calculator.get_big_grey_positions(): Light(world, factory.from_surface(light_sprites.get('big_grey')), pos[0], pos[1], -2) # draw small grey lights for pos in calculator.get_small_grey_positions(): Light(world, factory.from_surface(light_sprites.get('small_grey')), pos[0], pos[1], -2) _lights = {index: set() for index in range(5)} while True: # listen for exit event events = sdl2.ext.get_events() for event in events: if event.type == sdl2.SDL_QUIT: sys.exit(0) changes = calculator.get_changes() for index, change in changes.items(): if change['changed']: for entity in _lights[index]: world.delete(entity) _lights[index] = set() for position in change['positions']: _lights[index].add( Light( world, factory.from_surface( light_sprites.get(row_lights[index])), position[0], position[1])) world.process() sleep(1)
def _image2sprite(image, factory): # pylint: disable=missing-docstring,bad-continuation,protected-access # pylint: disable=too-many-locals,too-many-branches,too-many-statements mode = image.mode width, height = image.size rmask = gmask = bmask = amask = 0 if mode in ("1", "L", "P"): # 1 = B/W, 1 bit per byte # "L" = greyscale, 8-bit # "P" = palette-based, 8-bit pitch = width depth = 8 elif mode == "RGB": # 3x8-bit, 24bpp if endian.SDL_BYTEORDER == endian.SDL_LIL_ENDIAN: rmask = 0x0000FF gmask = 0x00FF00 bmask = 0xFF0000 else: rmask = 0xFF0000 gmask = 0x00FF00 bmask = 0x0000FF depth = 24 pitch = width * 3 elif mode in ("RGBA", "RGBX"): # RGBX: 4x8-bit, no alpha # RGBA: 4x8-bit, alpha if endian.SDL_BYTEORDER == endian.SDL_LIL_ENDIAN: rmask = 0x000000FF gmask = 0x0000FF00 bmask = 0x00FF0000 if mode == "RGBA": amask = 0xFF000000 else: rmask = 0xFF000000 gmask = 0x00FF0000 bmask = 0x0000FF00 if mode == "RGBA": amask = 0x000000FF depth = 32 pitch = width * 4 else: # We do not support CMYK or YCbCr for now raise TypeError("unsupported image format") pxbuf = image.tobytes() imgsurface = surface.SDL_CreateRGBSurfaceFrom(pxbuf, width, height, depth, pitch, rmask, gmask, bmask, amask) if not imgsurface: raise SDLError() imgsurface = imgsurface.contents # the pixel buffer must not be freed for the lifetime of the surface imgsurface._pxbuf = pxbuf if mode == "P": # Create a SDL_Palette for the SDL_Surface def _chunk(seq, size): for x in range(0, len(seq), size): yield seq[x:x + size] rgbcolors = image.getpalette() sdlpalette = pixels.SDL_AllocPalette(len(rgbcolors) // 3) if not sdlpalette: raise SDLError() SDL_Color = pixels.SDL_Color # pylint: disable=invalid-name for idx, (r, g, b) in enumerate(_chunk(rgbcolors, 3)): sdlpalette.contents.colors[idx] = SDL_Color(r, g, b) ret = surface.SDL_SetSurfacePalette(imgsurface, sdlpalette) # This will decrease the refcount on the palette, so it gets # freed properly on releasing the SDL_Surface. pixels.SDL_FreePalette(sdlpalette) if ret != 0: raise SDLError() return factory.from_surface(imgsurface, free=True)