1from cpython cimport PyObject 2from . import error 3from . import error as errorfnc 4from libc.stdlib cimport free, malloc 5 6 7WINDOWPOS_UNDEFINED = _SDL_WINDOWPOS_UNDEFINED 8WINDOWPOS_CENTERED = _SDL_WINDOWPOS_CENTERED 9 10MESSAGEBOX_ERROR = _SDL_MESSAGEBOX_ERROR 11MESSAGEBOX_WARNING = _SDL_MESSAGEBOX_WARNING 12MESSAGEBOX_INFORMATION = _SDL_MESSAGEBOX_INFORMATION 13 14 15cdef extern from "SDL.h" nogil: 16 Uint32 SDL_GetWindowPixelFormat(SDL_Window* window) 17 SDL_bool SDL_IntersectRect(const SDL_Rect* A, 18 const SDL_Rect* B, 19 SDL_Rect* result) 20 void SDL_SetWindowResizable(SDL_Window *window, SDL_bool resizable) 21 int SDL_GetWindowOpacity(SDL_Window *window, float *opacity) 22 int SDL_SetWindowOpacity(SDL_Window *window, float opacity) 23 int SDL_SetWindowModalFor(SDL_Window *modal_window, SDL_Window *parent_window) 24 int SDL_SetWindowInputFocus(SDL_Window *window) 25 int SDL_SetRelativeMouseMode(SDL_bool enabled) 26 SDL_bool SDL_GetRelativeMouseMode() 27 SDL_Renderer* SDL_GetRenderer(SDL_Window* window) 28 SDL_Window* SDL_GetWindowFromID(Uint32 id) 29 SDL_Surface * SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, Uint32 format) 30 31 32cdef extern from "pygame.h" nogil: 33 ctypedef struct pgSurfaceObject: 34 pass 35 36 int pgSurface_Check(object surf) 37 SDL_Surface* pgSurface_AsSurface(object surf) 38 void import_pygame_surface() 39 40 SDL_Window* pg_GetDefaultWindow() 41 void import_pygame_base() 42 43 int pgRect_Check(object rect) 44 SDL_Rect *pgRect_FromObject(object obj, SDL_Rect *temp) 45 object pgRect_New(SDL_Rect *r) 46 object pgRect_New4(int x, int y, int w, int h) 47 SDL_Rect pgRect_AsRect(object rect) 48 void import_pygame_rect() 49 50 51 object pgColor_New(Uint8 rgba[]) 52 object pgColor_NewLength(Uint8 rgba[], Uint8 length) 53 void import_pygame_color() 54 pgSurfaceObject *pgSurface_New2(SDL_Surface *info, int owner) 55 56cdef extern from "pgcompat.h" nogil: 57 pass 58 59import_pygame_base() 60import_pygame_color() 61import_pygame_surface() 62import_pygame_rect() 63 64class RendererDriverInfo: 65 def __repr__(self): 66 return "<%s(name: %s, flags: 0x%02x, num_texture_formats: %d, max_texture_width: %d, max_texture_height: %d)>" % ( 67 self.__class__.__name__, 68 self.name, 69 self.flags, 70 self.num_texture_formats, 71 self.max_texture_width, 72 self.max_texture_height, 73 ) 74 75def get_drivers(): 76 cdef int num = SDL_GetNumRenderDrivers() 77 cdef SDL_RendererInfo info 78 cdef int ind 79 for ind from 0 <= ind < num: 80 SDL_GetRenderDriverInfo(ind, &info) 81 ret = RendererDriverInfo() 82 ret.name = info.name 83 ret.flags = info.flags 84 ret.num_texture_formats = info.num_texture_formats 85 ret.max_texture_width = info.max_texture_width 86 ret.max_texture_height = info.max_texture_height 87 yield ret 88 89 90def get_grabbed_window(): 91 """return the Window with input grab enabled, 92 or None if input isn't grabbed.""" 93 cdef SDL_Window *win = SDL_GetGrabbedWindow() 94 cdef void *ptr 95 if win: 96 ptr = SDL_GetWindowData(win, "pg_window") 97 if not ptr: 98 return None 99 return <object>ptr 100 return None 101 102 103def messagebox(title, message, 104 Window window=None, 105 bint info=False, 106 bint warn=False, 107 bint error=False, 108 buttons=('OK', ), 109 return_button=0, 110 escape_button=0): 111 """ Display a message box. 112 113 :param str title: A title string or None. 114 :param str message: A message string. 115 :param bool info: If ``True``, display an info message. 116 :param bool warn: If ``True``, display a warning message. 117 :param bool error: If ``True``, display an error message. 118 :param tuple buttons: An optional sequence of buttons to show to the user (strings). 119 :param int return_button: Button index to use if the return key is hit (-1 for none). 120 :param int escape_button: Button index to use if the escape key is hit (-1 for none). 121 :return: The index of the button that was pushed. 122 """ 123 # TODO: type check 124 # TODO: color scheme 125 cdef SDL_MessageBoxButtonData* c_buttons = NULL 126 127 cdef SDL_MessageBoxData data 128 data.flags = 0 129 if warn: 130 data.flags |= _SDL_MESSAGEBOX_WARNING 131 if error: 132 data.flags |= _SDL_MESSAGEBOX_ERROR 133 if info: 134 data.flags |= _SDL_MESSAGEBOX_INFORMATION 135 if not window: 136 data.window = NULL 137 else: 138 data.window = window._win 139 if title is not None: 140 title = title.encode('utf8') 141 data.title = title 142 else: 143 data.title = NULL 144 message = message.encode('utf8') 145 data.message = message 146 data.colorScheme = NULL 147 148 cdef SDL_MessageBoxButtonData button 149 if not buttons: 150 button.flags = _SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT |\ 151 _SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT 152 button.buttonid = 0 153 button.text = "OK" 154 data.buttons = &button 155 data.numbuttons = 1 156 else: 157 buttons_utf8 = [s.encode('utf8') for s in buttons] 158 data.numbuttons = len(buttons) 159 c_buttons =\ 160 <SDL_MessageBoxButtonData*>malloc(data.numbuttons * sizeof(SDL_MessageBoxButtonData)) 161 if not c_buttons: 162 raise MemoryError() 163 for i, but in enumerate(reversed(buttons_utf8)): 164 c_buttons[i].flags = 0 165 c_buttons[i].buttonid = data.numbuttons - i - 1 166 if c_buttons[i].buttonid == return_button: 167 c_buttons[i].flags |= _SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT 168 if c_buttons[i].buttonid == escape_button: 169 c_buttons[i].flags |= _SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT 170 c_buttons[i].text = but 171 data.buttons = c_buttons 172 173 cdef int buttonid 174 if SDL_ShowMessageBox(&data, &buttonid): 175 free(c_buttons) 176 raise errorfnc() 177 178 free(c_buttons) 179 return buttonid 180 181 182cdef class Window: 183 DEFAULT_SIZE = 640, 480 184 185 _kwarg_to_flag = { 186 'opengl': _SDL_WINDOW_OPENGL, 187 'vulkan': _SDL_WINDOW_VULKAN, 188 'hidden': _SDL_WINDOW_HIDDEN, 189 'borderless': _SDL_WINDOW_BORDERLESS, 190 'resizable': _SDL_WINDOW_RESIZABLE, 191 'minimized': _SDL_WINDOW_MINIMIZED, 192 'maximized': _SDL_WINDOW_MAXIMIZED, 193 'input_grabbed': _SDL_WINDOW_INPUT_GRABBED, 194 'input_focus': _SDL_WINDOW_INPUT_FOCUS, 195 'mouse_focus': _SDL_WINDOW_MOUSE_FOCUS, 196 'allow_highdpi': _SDL_WINDOW_ALLOW_HIGHDPI, 197 'foreign': _SDL_WINDOW_FOREIGN, 198 'mouse_capture': _SDL_WINDOW_MOUSE_CAPTURE, 199 'always_on_top': _SDL_WINDOW_ALWAYS_ON_TOP, 200 'skip_taskbar': _SDL_WINDOW_SKIP_TASKBAR, 201 'utility': _SDL_WINDOW_UTILITY, 202 'tooltip': _SDL_WINDOW_TOOLTIP, 203 'popup_menu': _SDL_WINDOW_POPUP_MENU, 204 } 205 206 @classmethod 207 def from_display_module(cls): 208 cdef Window self = cls.__new__(cls) 209 cdef SDL_Window* window = pg_GetDefaultWindow() 210 if not window: 211 raise error() 212 self._win=window 213 self._is_borrowed=1 214 SDL_SetWindowData(window, "pg_window", <PyObject*>self) 215 return self 216 217 def __init__(self, title='pygame', 218 size=DEFAULT_SIZE, 219 position=WINDOWPOS_UNDEFINED, 220 bint fullscreen=False, 221 bint fullscreen_desktop=False, **kwargs): 222 """ Create a window with the specified position, dimensions, and flags. 223 224 :param str title: the title of the window, in UTF-8 encoding 225 :param tuple size: the size of the window, in screen coordinates (width, height) 226 :param position: a tuple specifying the window position, WINDOWPOS_CENTERED, or WINDOWPOS_UNDEFINED. 227 :param bool fullscreen: fullscreen window using the window size as the resolution (videomode change) 228 :param bool fullscreen_desktop: fullscreen window using the current desktop resolution 229 :param bool opengl: Usable with OpenGL context. You will still need to create an OpenGL context. 230 :param bool vulkan: usable with a Vulkan instance 231 :param bool hidden: window is not visible 232 :param bool borderless: no window decoration 233 :param bool resizable: window can be resized 234 :param bool minimized: window is minimized 235 :param bool maximized: window is maximized 236 :param bool input_grabbed: window has grabbed input focus 237 :param bool input_focus: window has input focus 238 :param bool mouse_focus: window has mouse focus 239 :param bool foreign: window not created by SDL 240 :param bool allow_highdpi: window should be created in high-DPI mode if supported (>= SDL 2.0.1) 241 :param bool mouse_capture: window has mouse captured (unrelated to INPUT_GRABBED, >= SDL 2.0.4) 242 :param bool always_on_top: window should always be above others (X11 only, >= SDL 2.0.5) 243 :param bool skip_taskbar: window should not be added to the taskbar (X11 only, >= SDL 2.0.5) 244 :param bool utility: window should be treated as a utility window (X11 only, >= SDL 2.0.5) 245 :param bool tooltip: window should be treated as a tooltip (X11 only, >= SDL 2.0.5) 246 :param bool popup_menu: window should be treated as a popup menu (X11 only, >= SDL 2.0.5) 247 """ 248 # https://wiki.libsdl.org/SDL_CreateWindow 249 # https://wiki.libsdl.org/SDL_WindowFlags 250 if position == WINDOWPOS_UNDEFINED: 251 x, y = WINDOWPOS_UNDEFINED, WINDOWPOS_UNDEFINED 252 elif position == WINDOWPOS_CENTERED: 253 x, y = WINDOWPOS_CENTERED, WINDOWPOS_CENTERED 254 else: 255 x, y = position 256 257 flags = 0 258 if fullscreen and fullscreen_desktop: 259 raise ValueError("fullscreen and fullscreen_desktop cannot be used at the same time.") 260 if fullscreen: 261 flags |= _SDL_WINDOW_FULLSCREEN 262 elif fullscreen_desktop: 263 flags |= _SDL_WINDOW_FULLSCREEN_DESKTOP 264 265 _kwarg_to_flag = self._kwarg_to_flag 266 for k, v in kwargs.items(): 267 try: 268 flag = _kwarg_to_flag[k] 269 if v: 270 flags |= flag 271 except KeyError: 272 raise TypeError("unknown parameter: %s" % k) 273 274 self._win = SDL_CreateWindow(title.encode('utf8'), x, y, 275 size[0], size[1], flags) 276 self._is_borrowed=0 277 if not self._win: 278 raise error() 279 SDL_SetWindowData(self._win, "pg_window", <PyObject*>self) 280 281 import pygame.pkgdata 282 surf = pygame.image.load(pygame.pkgdata.getResource( 283 'pygame_icon.bmp')) 284 surf.set_colorkey(0) 285 self.set_icon(surf) 286 287 @property 288 def grab(self): 289 """ Window's input grab state (``True`` or ``False``). 290 291 Set it to ``True`` to grab, ``False`` to release. 292 293 When input is grabbed the mouse is confined to the window. 294 If the caller enables a grab while another window is currently grabbed, 295 the other window loses its grab in favor of the caller's window. 296 297 :rtype: bool 298 """ 299 return SDL_GetWindowGrab(self._win) != 0 300 301 @grab.setter 302 def grab(self, bint grabbed): 303 # https://wiki.libsdl.org/SDL_SetWindowGrab 304 SDL_SetWindowGrab(self._win, 1 if grabbed else 0) 305 306 @property 307 def relative_mouse(self): 308 """ Window's relative mouse motion state (``True`` or ``False``). 309 310 Set it to ``True`` to enable, ``False`` to disable. 311 If mouse.set_visible(True) is set the input will be grabbed, 312 and the mouse will enter endless relative motion mode. 313 314 :rtype: bool 315 """ 316 return SDL_GetRelativeMouseMode() 317 318 319 @relative_mouse.setter 320 def relative_mouse(self, bint enable): 321 # https://wiki.libsdl.org/SDL_SetRelativeMouseMode 322 #SDL_SetWindowGrab(self._win, 1 if enable else 0) 323 SDL_SetRelativeMouseMode(1 if enable else 0) 324 325 def set_windowed(self): 326 """ Enable windowed mode 327 328 .. seealso:: :func:`set_fullscreen` 329 330 """ 331 # https://wiki.libsdl.org/SDL_SetWindowFullscreen 332 if SDL_SetWindowFullscreen(self._win, 0): 333 raise error() 334 335 #TODO: not sure about this... 336 # Perhaps this is more readable: 337 # window.fullscreen = True 338 # window.fullscreen_desktop = True 339 # window.windowed = True 340 def set_fullscreen(self, bint desktop=False): 341 """ Enable fullscreen for the window 342 343 :param bool desktop: If ``True``: use the current desktop resolution. 344 If ``False``: change the fullscreen resolution to the window size. 345 346 .. seealso:: :func:`set_windowed` 347 """ 348 cdef int flags = 0 349 if desktop: 350 flags = _SDL_WINDOW_FULLSCREEN_DESKTOP 351 else: 352 flags = _SDL_WINDOW_FULLSCREEN 353 if SDL_SetWindowFullscreen(self._win, flags): 354 raise error() 355 356 @property 357 def title(self): 358 """ The title of the window or u"" if there is none. 359 """ 360 # https://wiki.libsdl.org/SDL_GetWindowTitle 361 return SDL_GetWindowTitle(self._win).decode('utf8') 362 363 @title.setter 364 def title(self, title): 365 """ Set the window title. 366 367 :param str title: the desired window title in UTF-8. 368 """ 369 # https://wiki.libsdl.org/SDL_SetWindowTitle 370 SDL_SetWindowTitle(self._win, title.encode('utf8')) 371 372 def destroy(self): 373 """ Destroys the window. 374 """ 375 # https://wiki.libsdl.org/SDL_DestroyWindow 376 if self._win: 377 SDL_DestroyWindow(self._win) 378 self._win = NULL 379 380 def hide(self): 381 """ Hide the window. 382 """ 383 # https://wiki.libsdl.org/SDL_HideWindow 384 SDL_HideWindow(self._win) 385 386 def show(self): 387 """ Show the window. 388 """ 389 # https://wiki.libsdl.org/SDL_ShowWindow 390 SDL_ShowWindow(self._win) 391 392 def focus(self, input_only=False): 393 """ Raise the window above other windows and set the input focus. 394 395 :param bool input_only: if ``True``, the window will be given input focus 396 but may be completely obscured by other windows. 397 """ 398 # https://wiki.libsdl.org/SDL_RaiseWindow 399 if input_only: 400 if SDL_SetWindowInputFocus(self._win): 401 raise error() 402 else: 403 SDL_RaiseWindow(self._win) 404 405 def restore(self): 406 """ Restore the size and position of a minimized or maximized window. 407 """ 408 SDL_RestoreWindow(self._win) 409 410 def maximize(self): 411 """ Maximize the window. 412 """ 413 SDL_MaximizeWindow(self._win) 414 415 def minimize(self): 416 """ Minimize the window. 417 """ 418 SDL_MinimizeWindow(self._win) 419 420 @property 421 def resizable(self): 422 """ Sets whether the window is resizable. 423 """ 424 return SDL_GetWindowFlags(self._win) & _SDL_WINDOW_RESIZABLE != 0 425 426 @resizable.setter 427 def resizable(self, enabled): 428 SDL_SetWindowResizable(self._win, 1 if enabled else 0) 429 430 @property 431 def borderless(self): 432 """ Add or remove the border from the actual window. 433 434 .. note:: You can't change the border state of a fullscreen window. 435 """ 436 return SDL_GetWindowFlags(self._win) & _SDL_WINDOW_BORDERLESS != 0 437 438 @borderless.setter 439 def borderless(self, enabled): 440 SDL_SetWindowBordered(self._win, 1 if enabled else 0) 441 442 def set_icon(self, surface): 443 """ Set the icon for the window. 444 445 :param pygame.Surface surface: A Surface to use as the icon. 446 """ 447 if not pgSurface_Check(surface): 448 raise TypeError('surface must be a Surface object') 449 SDL_SetWindowIcon(self._win, pgSurface_AsSurface(surface)) 450 451 @property 452 def id(self): 453 """ A unique window ID. *Read-only*. 454 455 :rtype: int 456 """ 457 return SDL_GetWindowID(self._win) 458 459 @property 460 def size(self): 461 """ The size of the window's client area.""" 462 cdef int w, h 463 SDL_GetWindowSize(self._win, &w, &h) 464 return (w, h) 465 466 @size.setter 467 def size(self, size): 468 SDL_SetWindowSize(self._win, size[0], size[1]) 469 470 @property 471 def position(self): 472 """ Window's screen coordinates, or WINDOWPOS_CENTERED or WINDOWPOS_UNDEFINED""" 473 cdef int x, y 474 SDL_GetWindowPosition(self._win, &x, &y) 475 return (x, y) 476 477 @position.setter 478 def position(self, position): 479 cdef int x, y 480 if position == WINDOWPOS_UNDEFINED: 481 x, y = WINDOWPOS_UNDEFINED, WINDOWPOS_UNDEFINED 482 elif position == WINDOWPOS_CENTERED: 483 x, y = WINDOWPOS_CENTERED, WINDOWPOS_CENTERED 484 else: 485 x, y = position 486 SDL_SetWindowPosition(self._win, x, y) 487 488 @property 489 def opacity(self): 490 """ Window opacity. It ranges between 0.0 (fully transparent) 491 and 1.0 (fully opaque).""" 492 cdef float opacity 493 if SDL_GetWindowOpacity(self._win, &opacity): 494 raise error() 495 return opacity 496 497 @opacity.setter 498 def opacity(self, opacity): 499 if SDL_SetWindowOpacity(self._win, opacity): 500 raise error() 501 502 @property 503 def brightness(self): 504 """ The brightness (gamma multiplier) for the display that owns a given window. 505 0.0 is completely dark and 1.0 is normal brightness.""" 506 return SDL_GetWindowBrightness(self._win) 507 508 @brightness.setter 509 def brightness(self, float value): 510 if SDL_SetWindowBrightness(self._win, value): 511 raise error() 512 513 @property 514 def display_index(self): 515 """ The index of the display associated with the window. *Read-only*. 516 517 :rtype: int 518 """ 519 cdef int index = SDL_GetWindowDisplayIndex(self._win) 520 if index < 0: 521 raise error() 522 return index 523 524 def set_modal_for(self, Window parent): 525 """set the window as a modal for a parent window 526 This function is only supported on X11.""" 527 if SDL_SetWindowModalFor(self._win, parent._win): 528 raise error() 529 530 def __dealloc__(self): 531 if self._is_borrowed: 532 return 533 self.destroy() 534 535cdef Uint32 format_from_depth(int depth): 536 cdef Uint32 Rmask, Gmask, Bmask, Amask 537 if depth == 16: 538 Rmask = 0xF << 8 539 Gmask = 0xF << 4 540 Bmask = 0xF 541 Amask = 0xF << 12 542 elif depth in (0, 32): 543 Rmask = 0xFF << 16 544 Gmask = 0xFF << 8 545 Bmask = 0xFF 546 Amask = 0xFF << 24 547 else: 548 raise ValueError("no standard masks exist for given bitdepth with alpha") 549 return SDL_MasksToPixelFormatEnum(depth, 550 Rmask, Gmask, Bmask, Amask) 551 552 553cdef class Texture: 554 def __cinit__(self): 555 cdef Uint8[4] defaultColor = [255, 255, 255, 255] 556 self._color = pgColor_NewLength(defaultColor, 3) 557 558 def __init__(self, 559 Renderer renderer, 560 size, int depth=0, 561 static=False, streaming=False, 562 target=False): 563 """ Create an empty texture. 564 565 :param Renderer renderer: Rendering context for the texture. 566 :param tuple size: The width and height of the texture. 567 :param int depth: The pixel format (0 to use the default). 568 569 One of ``static``, ``streaming``, or ``target`` can be set 570 to ``True``. If all are ``False``, then ``static`` is used. 571 572 :param bool static: Changes rarely, not lockable. 573 :param bool streaming: Changes frequently, lockable. 574 :param bool target: Can be used as a render target. 575 """ 576 # https://wiki.libsdl.org/SDL_CreateTexture 577 # TODO: masks 578 cdef Uint32 format 579 try: 580 format = format_from_depth(depth) 581 except ValueError as e: 582 raise e 583 584 cdef int width, height 585 if len(size) != 2: 586 raise ValueError('size must have two elements') 587 width, height = size[0], size[1] 588 if width <= 0 or height <= 0: 589 raise ValueError('size must contain two positive values') 590 591 cdef int access 592 if static: 593 if streaming or target: 594 raise ValueError('only one of static, streaming, or target can be true') 595 access = _SDL_TEXTUREACCESS_STATIC 596 elif streaming: 597 if static or target: 598 raise ValueError('only one of static, streaming, or target can be true') 599 access = _SDL_TEXTUREACCESS_STREAMING 600 elif target: 601 if streaming or static: 602 raise ValueError('only one of static, streaming, or target can be true') 603 access = _SDL_TEXTUREACCESS_TARGET 604 else: 605 # Create static texture by default. 606 access = _SDL_TEXTUREACCESS_STATIC 607 608 self.renderer = renderer 609 cdef SDL_Renderer* _renderer = renderer._renderer 610 self._tex = SDL_CreateTexture(_renderer, 611 format, 612 access, 613 width, height) 614 if not self._tex: 615 raise error() 616 self.width, self.height = width, height 617 618 @staticmethod 619 def from_surface(Renderer renderer, surface): 620 """ Create a texture from an existing surface. 621 622 :param Renderer renderer: Rendering context for the texture. 623 :param pygame.Surface surface: The surface to create a texture from. 624 """ 625 # https://wiki.libsdl.org/SDL_CreateTextureFromSurface 626 if not pgSurface_Check(surface): 627 raise TypeError('2nd argument must be a surface') 628 cdef Texture self = Texture.__new__(Texture) 629 self.renderer = renderer 630 cdef SDL_Renderer* _renderer = renderer._renderer 631 cdef SDL_Surface *surf_ptr = pgSurface_AsSurface(surface) 632 self._tex = SDL_CreateTextureFromSurface(_renderer, 633 surf_ptr) 634 if not self._tex: 635 raise error() 636 self.width = surface.get_width() 637 self.height = surface.get_height() 638 return self 639 640 def __dealloc__(self): 641 if self._tex: 642 SDL_DestroyTexture(self._tex) 643 644 @property 645 def alpha(self): 646 # https://wiki.libsdl.org/SDL_GetTextureAlphaMod 647 cdef Uint8 alpha 648 res = SDL_GetTextureAlphaMod(self._tex, &alpha) 649 if res < 0: 650 raise error() 651 652 return alpha 653 654 @alpha.setter 655 def alpha(self, Uint8 new_value): 656 # https://wiki.libsdl.org/SDL_SetTextureAlphaMod 657 res = SDL_SetTextureAlphaMod(self._tex, new_value) 658 if res < 0: 659 raise error() 660 661 @property 662 def blend_mode(self): 663 # https://wiki.libsdl.org/SDL_GetTextureBlendMode 664 cdef SDL_BlendMode blendMode 665 res = SDL_GetTextureBlendMode(self._tex, &blendMode) 666 if res < 0: 667 raise error() 668 669 return blendMode 670 671 @blend_mode.setter 672 def blend_mode(self, blendMode): 673 # https://wiki.libsdl.org/SDL_SetTextureBlendMode 674 res = SDL_SetTextureBlendMode(self._tex, blendMode) 675 if res < 0: 676 raise error() 677 678 @property 679 def color(self): 680 # https://wiki.libsdl.org/SDL_GetTextureColorMod 681 res = SDL_GetTextureColorMod(self._tex, 682 &self._color.data[0], 683 &self._color.data[1], 684 &self._color.data[2]) 685 if res < 0: 686 raise error() 687 688 return self._color 689 690 @color.setter 691 def color(self, new_value): 692 # https://wiki.libsdl.org/SDL_SetTextureColorMod 693 res = SDL_SetTextureColorMod(self._tex, 694 new_value[0], 695 new_value[1], 696 new_value[2]) 697 if res < 0: 698 raise error() 699 700 def get_rect(self, **kwargs): 701 """ Get the rectangular area of the texture. 702 like surface.get_rect(), returns a new rectangle covering the entire surface. 703 This rectangle will always start at 0, 0 with a width. and height the same size as the texture. 704 """ 705 rect = pgRect_New4(0, 0, self.width, self.height) 706 for key in kwargs: 707 setattr(rect, key, kwargs[key]) 708 709 return rect 710 711 cdef draw_internal(self, SDL_Rect *csrcrect, SDL_Rect *cdstrect, float angle=0, SDL_Point *originptr=NULL, 712 bint flipX=False, bint flipY=False): 713 cdef int flip = SDL_FLIP_NONE 714 if flipX: 715 flip |= SDL_FLIP_HORIZONTAL 716 if flipY: 717 flip |= SDL_FLIP_VERTICAL 718 719 res = SDL_RenderCopyEx(self.renderer._renderer, self._tex, csrcrect, cdstrect, 720 angle, originptr, <SDL_RendererFlip>flip) 721 if res < 0: 722 raise error() 723 724 cpdef void draw(self, srcrect=None, dstrect=None, float angle=0, origin=None, 725 bint flipX=False, bint flipY=False): 726 """ Copy a portion of the texture to the rendering target. 727 728 :param srcrect: source rectangle on the texture, or None for the entire texture. 729 :param dstrect: destination rectangle or position on the render target, or None for entire target. 730 The texture is stretched to fill dstrect. 731 :param float angle: angle (in degrees) to rotate dstrect around (clockwise). 732 :param origin: point around which dstrect will be rotated. 733 If None, it will equal the center: (dstrect.w/2, dstrect.h/2). 734 :param bool flipX: flip horizontally. 735 :param bool flipY: flip vertically. 736 """ 737 cdef SDL_Rect src, dst 738 cdef SDL_Rect *csrcrect = NULL 739 cdef SDL_Rect *cdstrect = NULL 740 cdef SDL_Point corigin 741 cdef SDL_Point *originptr 742 743 if srcrect is not None: 744 csrcrect = pgRect_FromObject(srcrect, &src) 745 if not csrcrect: 746 raise TypeError("the argument is not a rectangle or None") 747 748 if dstrect is not None: 749 cdstrect = pgRect_FromObject(dstrect, &dst) 750 if cdstrect == NULL: 751 if len(dstrect) == 2: 752 dst.x = dstrect[0] 753 dst.y = dstrect[1] 754 dst.w = self.width 755 dst.h = self.height 756 cdstrect = &dst 757 else: 758 raise TypeError('dstrect must be a position, rect, or None') 759 760 if origin: 761 originptr = &corigin 762 corigin.x = origin[0] 763 corigin.y = origin[1] 764 else: 765 originptr = NULL 766 767 self.draw_internal(csrcrect, cdstrect, angle, originptr, 768 flipX, flipY) 769 770 def update(self, surface, area=None): 771 # https://wiki.libsdl.org/SDL_UpdateTexture 772 # Should it accept a raw pixel data array too? 773 """ Update the texture with Surface. 774 This is a fairly slow function, intended for use with static textures that do not change often. 775 776 If the texture is intended to be updated often, 777 it is preferred to create the texture as streaming and use the locking functions. 778 779 While this function will work with streaming textures, 780 for optimization reasons you may not get the pixels back if you lock the texture afterward. 781 782 :param surface: source Surface. 783 """ 784 785 if not pgSurface_Check(surface): 786 raise TypeError("update source should be a Surface.") 787 788 cdef SDL_Rect rect 789 cdef SDL_Rect *rectptr = pgRect_FromObject(area, &rect) 790 cdef SDL_Surface *surf = pgSurface_AsSurface(surface) 791 792 if rectptr == NULL and area is not None: 793 raise TypeError('area must be a rectangle or None') 794 795 res = SDL_UpdateTexture(self._tex, rectptr, surf.pixels, surf.pitch) 796 if res < 0: 797 raise error() 798 799cdef class Image: 800 801 def __cinit__(self): 802 self.angle = 0 803 self._origin.x = 0 804 self._origin.y = 0 805 self._originptr = NULL 806 self.flipX = False 807 self.flipY = False 808 809 cdef Uint8[4] defaultColor = [255, 255, 255, 255] 810 self._color = pgColor_NewLength(defaultColor, 3) 811 self.alpha = 255 812 813 def __init__(self, textureOrImage, srcrect=None): 814 cdef SDL_Rect temp 815 cdef SDL_Rect *rectptr 816 817 if isinstance(textureOrImage, Image): 818 self.texture = textureOrImage.texture 819 self.srcrect = pgRect_New(&(<Rect>textureOrImage.srcrect).r) 820 else: 821 self.texture = textureOrImage 822 self.srcrect = textureOrImage.get_rect() 823 self.blend_mode = textureOrImage.blend_mode 824 825 if srcrect is not None: 826 rectptr = pgRect_FromObject(srcrect, &temp) 827 if rectptr == NULL: 828 raise TypeError('srcrect must be None or a rectangle') 829 temp.x = rectptr.x 830 temp.y = rectptr.y 831 temp.w = rectptr.w 832 temp.h = rectptr.h 833 834 if temp.x < 0 or temp.y < 0 or \ 835 temp.w < 0 or temp.h < 0 or \ 836 temp.x + temp.w > self.srcrect.w or \ 837 temp.y + temp.h > self.srcrect.h: 838 raise ValueError('rect values are out of range') 839 temp.x += self.srcrect.x 840 temp.y += self.srcrect.y 841 self.srcrect = pgRect_New(&temp) 842 843 @property 844 def color(self): 845 return self._color 846 847 @color.setter 848 def color(self, new_color): 849 self._color[:3] = new_color[:3] 850 851 @property 852 def origin(self): 853 if self._originptr == NULL: 854 return None 855 else: 856 return (self._origin.x, self._origin.y) 857 858 @origin.setter 859 def origin(self, new_origin): 860 if new_origin: 861 self._origin.x = <int>new_origin[0] 862 self._origin.y = <int>new_origin[1] 863 self._originptr = &self._origin 864 else: 865 self._originptr = NULL 866 867 def get_rect(self): 868 return pgRect_New(&self.srcrect.r) 869 870 cpdef void draw(self, srcrect=None, dstrect=None): 871 """ Copy a portion of the image to the rendering target. 872 873 :param srcrect: source rectangle specifying a sub-image, or None for the entire image. 874 :param dstrect: destination rectangle or position on the render target, or None for entire target. 875 The image is stretched to fill dstrect. 876 """ 877 cdef SDL_Rect src 878 cdef SDL_Rect dst 879 cdef SDL_Rect *csrcrect = NULL 880 cdef SDL_Rect *cdstrect = NULL 881 cdef SDL_Rect *rectptr 882 883 if srcrect is None: 884 csrcrect = &self.srcrect.r 885 else: 886 if pgRect_Check(srcrect): 887 src = (<Rect>srcrect).r 888 else: 889 890 rectptr = pgRect_FromObject(srcrect, &src) 891 if rectptr == NULL: 892 raise TypeError('srcrect must be a rect or None') 893 src.x = rectptr.x 894 src.y = rectptr.y 895 src.w = rectptr.w 896 src.h = rectptr.h 897 898 src.x += self.srcrect.x 899 src.y += self.srcrect.y 900 csrcrect = &src 901 902 if dstrect is not None: 903 cdstrect = pgRect_FromObject(dstrect, &dst) 904 if cdstrect == NULL: 905 if len(dstrect) == 2: 906 dst.x = dstrect[0] 907 dst.y = dstrect[1] 908 dst.w = self.srcrect.w 909 dst.h = self.srcrect.h 910 cdstrect = &dst 911 else: 912 raise TypeError('dstrect must be a position, rect, or None') 913 914 self.texture.color = self._color 915 self.texture.alpha = self.alpha 916 self.texture.blend_mode = self.blend_mode 917 918 self.texture.draw_internal(csrcrect, cdstrect, self.angle, 919 self._originptr, self.flipX, self.flipY) 920 921 922cdef class Renderer: 923 924 @classmethod 925 def from_window(cls, Window window): 926 cdef Renderer self = cls.__new__(cls) 927 self._win = window 928 if window._is_borrowed: 929 self._is_borrowed=1 930 else: 931 raise error() 932 if not self._win: 933 raise error() 934 935 self._renderer = SDL_GetRenderer(self._win._win) 936 if not self._renderer: 937 raise error() 938 939 cdef Uint8[4] defaultColor = [255, 255, 255, 255] 940 self._draw_color = pgColor_NewLength(defaultColor, 4) 941 self._target = None 942 return self 943 944 def __init__(self, Window window, int index=-1, 945 int accelerated=-1, bint vsync=False, 946 bint target_texture=False): 947 """ Create a 2D rendering context for a window. 948 949 :param Window window: where rendering is displayed. 950 :param int index: index of rendering driver to initialize, 951 or -1 to init the first supporting requested options. 952 :param int accelerated: if 1, the renderer uses hardware acceleration. 953 if 0, the renderer is a software fallback. 954 -1 gives precedence to renderers using hardware acceleration. 955 :param bool vsync: .present() is synchronized with the refresh rate. 956 :param bool target_texture: the renderer supports rendering to texture. 957 """ 958 # https://wiki.libsdl.org/SDL_CreateRenderer 959 # https://wiki.libsdl.org/SDL_RendererFlags 960 flags = 0 961 if accelerated >= 0: 962 flags |= _SDL_RENDERER_ACCELERATED if accelerated else _SDL_RENDERER_SOFTWARE 963 if vsync: 964 flags |= _SDL_RENDERER_PRESENTVSYNC 965 if target_texture: 966 flags |= _SDL_RENDERER_TARGETTEXTURE 967 968 self._renderer = SDL_CreateRenderer(window._win, index, flags) 969 if not self._renderer: 970 raise error() 971 972 cdef Uint8[4] defaultColor = [255, 255, 255, 255] 973 self._draw_color = pgColor_NewLength(defaultColor, 4) 974 self._target = None 975 self._win = window 976 self._is_borrowed=0 977 978 def __dealloc__(self): 979 if self._is_borrowed: 980 return 981 if self._renderer: 982 SDL_DestroyRenderer(self._renderer) 983 984 @property 985 def draw_blend_mode(self): 986 # https://wiki.libsdl.org/SDL_GetRenderDrawBlendMode 987 cdef SDL_BlendMode blendMode 988 res = SDL_GetRenderDrawBlendMode(self._renderer, &blendMode) 989 if res < 0: 990 raise error() 991 992 return blendMode 993 994 @draw_blend_mode.setter 995 def draw_blend_mode(self, blendMode): 996 # https://wiki.libsdl.org/SDL_SetRenderDrawBlendMode 997 res = SDL_SetRenderDrawBlendMode(self._renderer, blendMode) 998 if res < 0: 999 raise error() 1000 1001 @property 1002 def draw_color(self): 1003 """ Color used by the drawing functions. 1004 """ 1005 return self._draw_color 1006 1007 @draw_color.setter 1008 def draw_color(self, new_value): 1009 """ color used by the drawing functions. 1010 """ 1011 # https://wiki.libsdl.org/SDL_SetRenderDrawColor 1012 self._draw_color[:] = new_value 1013 res = SDL_SetRenderDrawColor(self._renderer, 1014 new_value[0], 1015 new_value[1], 1016 new_value[2], 1017 new_value[3]) 1018 if res < 0: 1019 raise error() 1020 1021 def clear(self): 1022 """ Clear the current rendering target with the drawing color. 1023 """ 1024 # https://wiki.libsdl.org/SDL_RenderClear 1025 res = SDL_RenderClear(self._renderer) 1026 if res < 0: 1027 raise error() 1028 1029 def present(self): 1030 """ Present the composed backbuffer to the screen. 1031 1032 Updates the screen with any rendering performed since previous call. 1033 """ 1034 # https://wiki.libsdl.org/SDL_RenderPresent 1035 SDL_RenderPresent(self._renderer) 1036 1037 cpdef get_viewport(self): 1038 """ Returns the drawing area on the target. 1039 1040 :rtype: pygame.Rect 1041 """ 1042 # https://wiki.libsdl.org/SDL_RenderGetViewport 1043 cdef SDL_Rect rect 1044 SDL_RenderGetViewport(self._renderer, &rect) 1045 return pgRect_New(&rect) 1046 1047 @property 1048 def logical_size(self): 1049 cdef int w 1050 cdef int h 1051 SDL_RenderGetLogicalSize(self._renderer, &w, &h) 1052 return (w, h) 1053 1054 @logical_size.setter 1055 def logical_size(self, size): 1056 cdef int w = size[0] 1057 cdef int h = size[1] 1058 if (SDL_RenderSetLogicalSize(self._renderer, w, h) != 0): 1059 raise error() 1060 1061 @property 1062 def scale(self): 1063 cdef float x 1064 cdef float y 1065 SDL_RenderGetScale(self._renderer, &x, &y); 1066 return (x, y) 1067 1068 @scale.setter 1069 def scale(self, scale): 1070 cdef float x = scale[0] 1071 cdef float y = scale[1] 1072 if (SDL_RenderSetScale(self._renderer, x, y) != 0): 1073 raise error() 1074 1075 # TODO ifdef 1076 # def is_integer_scale(self): 1077 # return SDL_RenderGetIntegerScale(self._renderer) 1078 1079 def set_viewport(self, area): 1080 """ Set the drawing area on the target. 1081 If this is set to ``None``, the entire target will be used. 1082 1083 :param area: A ``pygame.Rect`` or tuple representing the 1084 drawing area on the target, or None. 1085 """ 1086 # https://wiki.libsdl.org/SDL_RenderSetViewport 1087 if area is None: 1088 if SDL_RenderSetViewport(self._renderer, NULL) < 0: 1089 raise error() 1090 return 1091 1092 cdef SDL_Rect tmprect 1093 cdef SDL_Rect *rectptr = pgRect_FromObject(area, &tmprect) 1094 if rectptr == NULL: 1095 raise TypeError('expected a rectangle') 1096 1097 if SDL_RenderSetViewport(self._renderer, rectptr) < 0: 1098 raise error() 1099 1100 1101 @property 1102 def target(self): 1103 """ The current render target. Set to ``None`` for the default target. 1104 1105 :rtype: Texture, None 1106 """ 1107 # https://wiki.libsdl.org/SDL_GetRenderTarget 1108 return self._target 1109 1110 @target.setter 1111 def target(self, newtarget): 1112 # https://wiki.libsdl.org/SDL_SetRenderTarget 1113 if newtarget is None: 1114 self._target = None 1115 if SDL_SetRenderTarget(self._renderer, NULL) < 0: 1116 raise error() 1117 elif isinstance(newtarget, Texture): 1118 self._target = newtarget 1119 if SDL_SetRenderTarget(self._renderer, 1120 self._target._tex) < 0: 1121 raise error() 1122 else: 1123 raise TypeError('target must be a Texture or None') 1124 1125 cpdef object blit(self, object source, Rect dest=None, Rect area=None, int special_flags=0): 1126 """ Only for compatibility. 1127 Textures created by different Renderers cannot shared with each other! 1128 :param source: A Texture or Image to draw. 1129 :param dest: destination on the render target. 1130 :param area: the portion of source texture. 1131 :param special_flags: have no effect at this moment. 1132 """ 1133 if isinstance(source, Texture): 1134 (<Texture>source).draw(area, dest) 1135 elif isinstance(source, Image): 1136 (<Image>source).draw(area, dest) 1137 elif not hasattr(source, 'draw'): 1138 raise TypeError('source must be drawable') 1139 else: 1140 source.draw(area, dest) 1141 1142 if not dest: 1143 return self.get_viewport() 1144 return dest 1145 1146 def draw_line(self, p1, p2): 1147 # https://wiki.libsdl.org/SDL_RenderDrawLine 1148 res = SDL_RenderDrawLine(self._renderer, 1149 p1[0], p1[1], 1150 p2[0], p2[1]) 1151 if res < 0: 1152 raise error() 1153 1154 def draw_point(self, point): 1155 # https://wiki.libsdl.org/SDL_RenderDrawPoint 1156 res = SDL_RenderDrawPoint(self._renderer, 1157 point[0], point[1]) 1158 if res < 0: 1159 raise error() 1160 1161 def draw_rect(self, rect): 1162 # https://wiki.libsdl.org/SDL_RenderDrawRect 1163 cdef SDL_Rect _rect 1164 cdef SDL_Rect *rectptr = pgRect_FromObject(rect, &_rect) 1165 if rectptr == NULL: 1166 raise TypeError('expected a rectangle') 1167 res = SDL_RenderDrawRect(self._renderer, rectptr) 1168 if res < 0: 1169 raise error() 1170 1171 def fill_rect(self, rect): 1172 # https://wiki.libsdl.org/SDL_RenderFillRect 1173 cdef SDL_Rect _rect 1174 cdef SDL_Rect *rectptr = pgRect_FromObject(rect, &_rect) 1175 if rectptr == NULL: 1176 raise TypeError('expected a rectangle') 1177 res = SDL_RenderFillRect(self._renderer, rectptr) 1178 1179 if res < 0: 1180 raise error() 1181 1182 def to_surface(self, surface=None, area=None): 1183 # https://wiki.libsdl.org/SDL_RenderReadPixels 1184 """ 1185 Read pixels from the current rendering target and create a pygame.Surface. 1186 WARNING: This is a very slow operation, and should not be used frequently. 1187 1188 :param surface: A surface to read the pixel data into. 1189 It must be large enough to fit the area, or ``ValueError`` is 1190 raised. 1191 If ``None``, a new surface is returned. 1192 :param area: The area of the screen to read pixels from. The area is 1193 clipped to fit inside the viewport. 1194 If ``None``, the entire viewport is used. 1195 """ 1196 cdef Uint32 format 1197 cdef SDL_Rect rarea 1198 cdef SDL_Rect tempviewport 1199 cdef SDL_Rect *areaparam 1200 cdef SDL_Surface *surf 1201 cdef SDL_Rect *rectptr 1202 1203 # obtain area to use 1204 if area is not None: 1205 1206 rectptr = pgRect_FromObject(area, &rarea) 1207 if rectptr == NULL: 1208 raise TypeError('area must be None or a rect') 1209 1210 # clip area 1211 SDL_RenderGetViewport(self._renderer, &tempviewport) 1212 SDL_IntersectRect(rectptr, &tempviewport, rectptr) 1213 1214 areaparam = rectptr 1215 rarea.x = rectptr.x 1216 rarea.y = rectptr.y 1217 rarea.w = rectptr.w 1218 rarea.h = rectptr.h 1219 else: 1220 SDL_RenderGetViewport(self._renderer, &rarea) 1221 areaparam = NULL 1222 1223 # prepare surface 1224 if surface is None: 1225 # create a new surface 1226 format = SDL_GetWindowPixelFormat(self._win._win) 1227 if format == SDL_PIXELFORMAT_UNKNOWN: 1228 raise error() 1229 1230 surf = SDL_CreateRGBSurfaceWithFormat( 1231 0, 1232 rarea.w, rarea.h, 1233 SDL_BITSPERPIXEL(format), 1234 format) 1235 if surf == NULL: 1236 raise MemoryError("not enough memory for the surface") 1237 1238 surface = <object>pgSurface_New2(surf, 1) 1239 elif pgSurface_Check(surface): 1240 surf = pgSurface_AsSurface(surface) 1241 if surf.w < rarea.w or surf.h < rarea.h: 1242 raise ValueError("the surface is too small") 1243 format = surf.format.format 1244 else: 1245 raise TypeError("'surface' must be a surface or None") 1246 1247 if SDL_RenderReadPixels(self._renderer, 1248 areaparam, 1249 format, surf.pixels, surf.pitch) < 0: 1250 raise error() 1251 return surface 1252