1# Copyright 2014 Patrick Dawson <pat@dw.is> 2# Copyright 2014 Tom Rothamel <tom@rothamel.us> 3# 4# This software is provided 'as-is', without any express or implied 5# warranty. In no event will the authors be held liable for any damages 6# arising from the use of this software. 7# 8# Permission is granted to anyone to use this software for any purpose, 9# including commercial applications, and to alter it and redistribute it 10# freely, subject to the following restrictions: 11# 12# 1. The origin of this software must not be misrepresented; you must not 13# claim that you wrote the original software. If you use this software 14# in a product, an acknowledgment in the product documentation would be 15# appreciated but is not required. 16# 2. Altered source versions must be plainly marked as such, and must not be 17# misrepresented as being the original software. 18# 3. This notice may not be removed or altered from any source distribution. 19 20from libc.string cimport memmove 21from sdl2 cimport * 22 23from pygame_sdl2.color cimport map_color, get_color 24from pygame_sdl2.rect cimport to_sdl_rect 25from pygame_sdl2.rect import Rect 26 27from pygame_sdl2.error import error 28from pygame_sdl2.locals import SRCALPHA 29import pygame_sdl2 30 31import warnings 32 33cdef extern from "src/surface.h" nogil: 34 int pygame_Blit (SDL_Surface * src, SDL_Rect * srcrect, 35 SDL_Surface * dst, SDL_Rect * dstrect, int the_args); 36 37cdef void move_pixels(Uint8 *src, Uint8 *dst, int h, int span, int srcpitch, int dstpitch) nogil: 38 if src < dst: 39 src += (h - 1) * srcpitch; 40 dst += (h - 1) * dstpitch; 41 srcpitch = -srcpitch; 42 dstpitch = -dstpitch; 43 44 while h: 45 h -= 1 46 memmove(dst, src, span); 47 src += srcpitch; 48 dst += dstpitch; 49 50# The total size of all allocated surfaces 51total_size = 0 52 53cdef class Surface: 54 55 def __cinit__(self): 56 self.surface = NULL 57 self.owns_surface = False 58 self.window_surface = False 59 self.has_alpha = False 60 61 def __dealloc__(self): 62 global total_size 63 64 if self.surface and self.owns_surface: 65 if total_size: 66 total_size -= self.surface.pitch * self.surface.h 67 68 SDL_FreeSurface(self.surface) 69 return 70 elif self.window_surface: 71 return 72 elif self.parent: 73 SDL_FreeSurface(self.surface) 74 return 75 76 warnings.warn("Memory leak via Surface in pygame_sdl2.") 77 78 def __sizeof__(self): 79 if self.surface and self.owns_surface: 80 return self.surface.pitch * self.surface.h 81 else: 82 return 0 83 84 def __init__(self, size, flags=0, depth=32, masks=None): 85 86 self.locklist = None 87 self.parent = None 88 89 self.offset_x = 0 90 self.offset_y = 0 91 92 self.get_window_flags = None 93 94 # When size is an empty tuple, we do not create a surface, and 95 # expect our caller to set this object up. 96 if size == (): 97 return 98 99 cdef int w 100 cdef int h 101 102 w, h = size 103 assert w >= 0 104 assert h >= 0 105 106 cdef Uint32 Rmask, Gmask, Bmask, Amask 107 cdef SDL_Surface *sample 108 cdef Surface pysample 109 cdef int depth_int 110 111 if masks is not None: 112 Rmask, Gmask, Bmask, Amask = masks 113 depth_int = depth 114 115 elif isinstance(depth, Surface): 116 117 pysample = depth 118 sample = pysample.surface 119 Rmask = sample.format.Rmask 120 Gmask = sample.format.Gmask 121 Bmask = sample.format.Bmask 122 Amask = sample.format.Amask 123 depth_int = sample.format.BitsPerPixel 124 125 else: 126 127 pysample = pygame_sdl2.display.get_surface() 128 129 if pysample and pysample.surface.format.BitsPerPixel == 32: 130 sample = pysample.surface 131 Rmask = sample.format.Rmask 132 Gmask = sample.format.Gmask 133 Bmask = sample.format.Bmask 134 Amask = sample.format.Amask 135 136 else: 137 138 # RGB(A) 139 if SDL_BYTEORDER == SDL_BIG_ENDIAN: 140 Rmask = 0xff000000 141 Gmask = 0x00ff0000 142 Bmask = 0x0000ff00 143 Amask = 0 144 else: 145 Rmask = 0x000000ff 146 Gmask = 0x0000ff00 147 Bmask = 0x00ff0000 148 Amask = 0 149 150 if (flags & SRCALPHA): 151 if not Amask: 152 Amask = 0xffffffff & ~(Rmask | Gmask | Bmask) 153 else: 154 Amask = 0 155 156 depth_int = 32 157 158 cdef SDL_Surface *surface 159 160 with nogil: 161 surface = SDL_CreateRGBSurface(0, w, h, depth_int, Rmask, Gmask, Bmask, Amask) 162 163 if not surface: 164 raise error() 165 166 self.take_surface(surface) 167 168 cdef void take_surface(self, SDL_Surface *surface): 169 if not surface: 170 raise error("A null pointer was passed in.") 171 172 self.surface = surface 173 self.owns_surface = True 174 175 global total_size 176 177 total_size += self.surface.pitch * self.surface.h 178 179 def __repr__(self): 180 return "<Surface({}x{}x{})>".format(self.surface.w, self.surface.h, self.surface.format.BitsPerPixel) 181 182 def blit(self, Surface source, dest, area=None, int special_flags=0): 183 cdef SDL_Rect dest_rect 184 cdef SDL_Rect area_rect 185 cdef SDL_Rect *area_ptr = NULL 186 187 cdef Surface temp 188 189 cdef int err 190 cdef Uint32 key 191 cdef Uint8 alpha 192 cdef bint colorkey 193 194 colorkey = (SDL_GetColorKey(source.surface, &key) == 0) 195 196 if not source.surface.format.Amask: 197 if SDL_GetSurfaceAlphaMod(source.surface, &alpha): 198 raise error() 199 200 if alpha != 255 and (self.surface.format.Amask or colorkey): 201 202 if area: 203 source = source.subsurface(area) 204 area = None 205 206 if SDL_SetSurfaceBlendMode(source.surface, SDL_BLENDMODE_NONE): 207 raise error() 208 temp = Surface(source.get_size(), SRCALPHA) 209 210 with nogil: 211 err = SDL_UpperBlit(source.surface, NULL, temp.surface, NULL) 212 213 if err: 214 raise error() 215 216 source = temp 217 colorkey = False 218 219 if colorkey: 220 if SDL_SetSurfaceBlendMode(source.surface, SDL_BLENDMODE_NONE): 221 raise error() 222 else: 223 if SDL_SetSurfaceBlendMode(source.surface, SDL_BLENDMODE_BLEND): 224 raise error() 225 226 to_sdl_rect(dest, &dest_rect, "dest") 227 228 if area is not None: 229 to_sdl_rect(area, &area_rect, "area") 230 area_ptr = &area_rect 231 232 with nogil: 233 234 if source.surface.format.Amask or special_flags: 235 err = pygame_Blit(source.surface, area_ptr, self.surface, &dest_rect, special_flags) 236 else: 237 err = SDL_UpperBlit(source.surface, area_ptr, self.surface, &dest_rect) 238 239 if err: 240 raise error() 241 242 dirty = Rect(dest[0], dest[1], source.surface.w, source.surface.h) 243 return dirty.clip(self.get_rect()) 244 245 def convert(self, surface=None): 246 if not isinstance(surface, Surface): 247 surface = pygame_sdl2.display.get_surface() 248 249 cdef SDL_PixelFormat *sample_format 250 251 if surface is None: 252 sample_format = SDL_AllocFormat(SDL_PIXELFORMAT_BGRX8888) 253 else: 254 sample_format = (<Surface> surface).surface.format 255 256 cdef Uint32 amask 257 cdef Uint32 rmask 258 cdef Uint32 gmask 259 cdef Uint32 bmask 260 261 cdef Uint32 pixel_format 262 cdef SDL_Surface *new_surface 263 264 # If the sample surface has alpha, use it. 265 if not sample_format.Amask: 266 use_format = sample_format 267 268 with nogil: 269 new_surface = SDL_ConvertSurface(self.surface, sample_format, 0) 270 271 if not new_surface: 272 raise error() 273 274 else: 275 276 rmask = sample_format.Rmask 277 gmask = sample_format.Gmask 278 bmask = sample_format.Bmask 279 amask = 0 280 281 pixel_format = SDL_MasksToPixelFormatEnum(32, rmask, gmask, bmask, amask) 282 283 with nogil: 284 new_surface = SDL_ConvertSurfaceFormat(self.surface, pixel_format, 0) 285 286 if not new_surface: 287 raise error() 288 289 cdef Surface rv = Surface(()) 290 rv.take_surface(new_surface) 291 292 return rv 293 294 def convert_alpha(self, Surface surface=None): 295 if surface is None: 296 surface = pygame_sdl2.display.get_surface() 297 298 cdef SDL_PixelFormat *sample_format 299 300 if surface is None: 301 sample_format = SDL_AllocFormat(SDL_PIXELFORMAT_BGRA8888) 302 else: 303 sample_format = (<Surface> surface).surface.format 304 305 cdef Uint32 amask = 0xff000000 306 cdef Uint32 rmask = 0x00ff0000 307 cdef Uint32 gmask = 0x0000ff00 308 cdef Uint32 bmask = 0x000000ff 309 310 cdef Uint32 pixel_format 311 cdef SDL_Surface *new_surface 312 313 # If the sample surface has alpha, use it. 314 if sample_format.Amask: 315 use_format = sample_format 316 317 with nogil: 318 new_surface = SDL_ConvertSurface(self.surface, sample_format, 0) 319 320 if not new_surface: 321 raise error() 322 323 else: 324 325 if sample_format.BytesPerPixel == 4: 326 rmask = sample_format.Rmask 327 gmask = sample_format.Gmask 328 bmask = sample_format.Bmask 329 amask = 0xffffffff & ~(rmask | gmask | bmask) 330 331 pixel_format = SDL_MasksToPixelFormatEnum(32, rmask, gmask, bmask, amask) 332 333 with nogil: 334 new_surface = SDL_ConvertSurfaceFormat(self.surface, pixel_format, 0) 335 336 if not new_surface: 337 raise error() 338 339 cdef Surface rv = Surface(()) 340 rv.take_surface(new_surface) 341 342 return rv 343 344 def copy(self): 345 if self.surface.format.Amask: 346 return self.convert_alpha(self) 347 else: 348 return self.convert(self) 349 350 def fill(self, color, rect=None, special_flags=0): 351 352 cdef SDL_Rect sdl_rect 353 cdef Uint32 pixel = map_color(self.surface, color) 354 cdef int err 355 356 if rect is not None: 357 to_sdl_rect(rect, &sdl_rect) 358 359 if sdl_rect.x < 0: 360 sdl_rect.w = sdl_rect.w + sdl_rect.x 361 sdl_rect.x = 0 362 363 if sdl_rect.y < 0: 364 sdl_rect.w = sdl_rect.h + sdl_rect.y 365 sdl_rect.y = 0 366 367 if sdl_rect.w <= 0 or sdl_rect.h <= 0: 368 return Rect(0, 0, 0, 0) 369 370 with nogil: 371 err = SDL_FillRect(self.surface, &sdl_rect, pixel) 372 373 if err: 374 raise error() 375 376 return Rect(sdl_rect.x, sdl_rect.y, sdl_rect.w, sdl_rect.h) 377 378 else: 379 with nogil: 380 err = SDL_FillRect(self.surface, NULL, pixel) 381 382 if err: 383 raise error() 384 385 return Rect(0, 0, self.surface.w, self.surface.h) 386 387 388 def scroll(self, int dx=0, int dy=0): 389 cdef int srcx, destx, move_width 390 cdef int srcy, desty, move_height 391 392 cdef int width = self.surface.w 393 cdef int height = self.surface.h 394 395 cdef int per_pixel = self.surface.format.BytesPerPixel 396 397 if dx >= 0: 398 srcx = 0 399 destx = dx 400 move_width = width - dx 401 else: 402 srcx = -dx 403 destx = 0 404 move_width = width + dx 405 406 if dy >= 0: 407 srcy = 0 408 desty = dy 409 move_height = height - dy 410 else: 411 srcy = -dy 412 desty = 0 413 move_height = height + dy 414 415 cdef Uint8 *srcptr = <Uint8 *> self.surface.pixels 416 cdef Uint8 *destptr = <Uint8 *> self.surface.pixels 417 418 srcptr += srcy * self.surface.pitch + srcx * per_pixel 419 destptr += desty * self.surface.pitch + destx * per_pixel 420 421 self.lock() 422 423 with nogil: 424 move_pixels( 425 srcptr, 426 destptr, 427 move_height, 428 move_width * per_pixel, 429 self.surface.pitch, 430 self.surface.pitch) 431 432 self.unlock() 433 434 def set_colorkey(self, color, flags=0): 435 436 if color is None: 437 if SDL_SetColorKey(self.surface, SDL_FALSE, 0): 438 raise error() 439 440 else: 441 if SDL_SetColorKey(self.surface, SDL_TRUE, map_color(self.surface, color)): 442 raise error() 443 444 def get_colorkey(self): 445 cdef Uint32 key 446 447 if SDL_GetColorKey(self.surface, &key): 448 return None 449 450 return get_color(key, self.surface) 451 452 def set_alpha(self, value, flags=0): 453 if value is None: 454 value = 255 455 self.has_alpha = False 456 else: 457 self.has_alpha = True 458 459 if SDL_SetSurfaceAlphaMod(self.surface, value): 460 raise error() 461 462 def get_alpha(self): 463 cdef Uint8 rv 464 465 if self.has_alpha or self.surface.format.Amask: 466 467 if SDL_GetSurfaceAlphaMod(self.surface, &rv): 468 raise error() 469 470 return rv 471 472 else: 473 return None 474 475 def lock(self, lock=None): 476 cdef Surface root = self 477 478 while root.parent: 479 root = root.parent 480 481 if lock is None: 482 lock = self 483 484 if root.locklist is None: 485 root.locklist = [ ] 486 487 root.locklist.append(lock) 488 489 if SDL_LockSurface(root.surface): 490 raise error() 491 492 def unlock(self, lock=None): 493 cdef Surface root = self 494 495 while root.parent: 496 root = root.parent 497 498 if lock is None: 499 lock = self 500 501 if root.locklist is None: 502 root.locklist = [ ] 503 504 root.locklist.remove(lock) 505 506 SDL_UnlockSurface(root.surface) 507 508 def mustlock(self): 509 cdef Surface root = self 510 511 while root.parent: 512 root = root.parent 513 514 return SDL_MUSTLOCK(root.surface) 515 516 def get_locked(self): 517 if self.locklist: 518 return True 519 520 def get_locks(self): 521 cdef Surface root = self 522 523 while root.parent: 524 root = root.parent 525 526 if root.locklist is None: 527 root.locklist = [ ] 528 529 return root.locklist 530 531 def get_at(self, pos): 532 cdef int x, y 533 cdef Uint8 *p 534 535 x, y = pos 536 537 if not (0 <= x < self.surface.w) or not (0 <= y < self.surface.h): 538 raise IndexError("Position outside surface.") 539 540 if self.surface.format.BytesPerPixel != 4: 541 raise error("Surface has unsupported bytesize.") 542 543 self.lock() 544 545 p = <Uint8 *> self.surface.pixels 546 p += y * self.surface.pitch 547 p += x * 4 548 549 cdef Uint32 pixel = (<Uint32 *> p)[0] 550 551 self.unlock() 552 553 return get_color(pixel, self.surface) 554 555 def set_at(self, pos, color): 556 cdef int x, y 557 cdef Uint8 *p 558 cdef Uint32 pixel 559 560 x, y = pos 561 562 if not (0 <= x < self.surface.w) or not (0 <= y < self.surface.h): 563 raise ValueError("Position outside surface.") 564 565 if self.surface.format.BytesPerPixel != 4: 566 raise error("Surface has unsupported bytesize.") 567 568 pixel = map_color(self.surface, color) 569 570 self.lock() 571 572 p = <Uint8 *> self.surface.pixels 573 p += y * self.surface.pitch 574 p += x * 4 575 576 (<Uint32 *> p)[0] = pixel 577 578 self.unlock() 579 580 def get_at_mapped(self, pos): 581 cdef int x, y 582 cdef Uint8 *p 583 584 x, y = pos 585 586 if not (0 <= x < self.surface.w) or not (0 <= y < self.surface.h): 587 raise ValueError("Position outside surface.") 588 589 if self.surface.format.BytesPerPixel != 4: 590 raise error("Surface has unsupported bytesize.") 591 592 self.lock() 593 594 p = <Uint8 *> self.surface.pixels 595 p += y * self.surface.pitch 596 p += x * 4 597 598 cdef Uint32 pixel = (<Uint32 *> p)[0] 599 600 self.unlock() 601 602 return pixel 603 604 def map_rgb(self, color): 605 return map_color(self.surface, color) 606 607 def unmap_rgb(self, pixel): 608 return get_color(pixel, self.surface) 609 610 def set_clip(self, rect): 611 cdef SDL_Rect sdl_rect 612 613 if rect is None: 614 SDL_SetClipRect(self.surface, NULL) 615 else: 616 to_sdl_rect(rect, &sdl_rect) 617 SDL_SetClipRect(self.surface, &sdl_rect) 618 619 def get_clip(self): 620 cdef SDL_Rect sdl_rect 621 622 SDL_GetClipRect(self.surface, &sdl_rect) 623 624 return (sdl_rect.x, sdl_rect.y, sdl_rect.w, sdl_rect.h) 625 626 def subsurface(self, *args): 627 cdef SDL_Rect sdl_rect 628 629 if len(args) == 1: 630 to_sdl_rect(args[0], &sdl_rect) 631 else: 632 to_sdl_rect(args, &sdl_rect) 633 634 if sdl_rect.w < 0 or sdl_rect.h < 0: 635 raise error("subsurface size must be non-negative.") 636 637 if ((sdl_rect.x < 0) 638 or (sdl_rect.y < 0) 639 or (sdl_rect.x + sdl_rect.w > self.surface.w) 640 or (sdl_rect.y + sdl_rect.h > self.surface.h)): 641 642 raise error("subsurface rectangle outside surface area.") 643 644 cdef Uint8 *pixels = <Uint8 *> self.surface.pixels 645 pixels += sdl_rect.y * self.surface.pitch 646 pixels += sdl_rect.x * self.surface.format.BytesPerPixel 647 648 cdef SDL_Surface *new_surface = SDL_CreateRGBSurfaceFrom( 649 pixels, 650 sdl_rect.w, 651 sdl_rect.h, 652 self.surface.format.BitsPerPixel, 653 self.surface.pitch, 654 self.surface.format.Rmask, 655 self.surface.format.Gmask, 656 self.surface.format.Bmask, 657 self.surface.format.Amask) 658 659 if not new_surface: 660 raise error() 661 662 cdef Surface rv = Surface(()) 663 664 rv.surface = new_surface 665 rv.parent = self 666 rv.offset_x = sdl_rect.x 667 rv.offset_y = sdl_rect.y 668 669 if self.has_alpha: 670 rv.set_alpha(self.get_alpha()) 671 672 return rv 673 674 def get_parent(self): 675 return self.parent 676 677 def get_abs_parent(self): 678 rv = self 679 680 while rv.parent: 681 rv = rv.parent 682 683 return rv 684 685 def get_offset(self): 686 return (self.offset_x, self.offset_y) 687 688 def get_abs_offset(self): 689 cdef Surface surf = self 690 691 cdef int offset_x = 0 692 cdef int offset_y = 0 693 694 while surf: 695 offset_x += surf.offset_x 696 offset_y += surf.offset_y 697 surf = surf.parent 698 699 return (offset_x, offset_y) 700 701 def get_size(self): 702 return self.surface.w, self.surface.h 703 704 def get_width(self): 705 return self.surface.w 706 707 def get_height(self): 708 return self.surface.h 709 710 def get_rect(self, **kwargs): 711 rv = Rect((0, 0, self.surface.w, self.surface.h)) 712 713 for k, v in kwargs.items(): 714 setattr(rv, k, v) 715 716 return rv 717 718 def get_bitsize(self): 719 return self.surface.format.BitsPerPixel 720 721 def get_bytesize(self): 722 return self.surface.format.BytesPerPixel 723 724 def get_flags(self): 725 726 if self.get_window_flags: 727 rv = self.get_window_flags() 728 else: 729 rv = 0 730 731 if self.surface.format.Amask or self.has_alpha: 732 rv = rv | SRCALPHA 733 734 return rv 735 736 def get_pitch(self): 737 return self.surface.pitch 738 739 def get_masks(self): 740 cdef SDL_PixelFormat *format = self.surface.format 741 return (format.Rmask, format.Gmask, format.Bmask, format.Amask) 742 743 def set_masks(self, masks): 744 warnings.warn("Surface.set_masks is not supported.") 745 746 def get_shifts(self): 747 cdef SDL_PixelFormat *format = self.surface.format 748 return (format.Rshift, format.Gshift, format.Bshift, format.Ashift) 749 750 def set_shifts(self, shifts): 751 warnings.warn("Surface.set_shifts is not supported.") 752 753 def get_shifts(self): 754 cdef SDL_PixelFormat *format = self.surface.format 755 return (format.Rshift, format.Gshift, format.Bshift, format.Ashift) 756 757 def get_losses(self): 758 cdef SDL_PixelFormat *format = self.surface.format 759 return (format.Rloss, format.Gloss, format.Bloss, format.Aloss) 760 761 def get_bounding_rect(self, min_alpha=1): 762 763 cdef Uint32 amask = self.surface.format.Amask 764 cdef Uint32 amin = (0x01010101 * min_alpha) & amask 765 766 cdef int x 767 cdef int y 768 cdef int w 769 cdef int h 770 771 cdef int minx = self.surface.w - 1 772 cdef int maxx = 0 773 cdef int miny = self.surface.h - 1 774 cdef int maxy = 0 775 776 cdef Uint32 *row 777 778 cdef Uint32 topleft 779 cdef Uint32 botright 780 781 if (not amask) or (self.surface.w == 0) or (self.surface.h == 0): 782 return Rect((0, 0, self.surface.w, self.surface.h)) 783 784 self.lock() 785 786 cdef Uint8 *pixels = <Uint8 *> self.surface.pixels 787 788 with nogil: 789 790 topleft = (<Uint32*> pixels)[0] 791 botright = (<Uint32*> (pixels + self.surface.pitch * (self.surface.h - 1)))[self.surface.w - 1] 792 793 if ((topleft & amask) > amin) and ((botright & amask) > amin): 794 795 # Bounding box covers image. 796 797 minx = 0 798 miny = 0 799 maxx = self.surface.w - 1 800 maxy = self.surface.h - 1 801 802 else: 803 804 # Bounding box is smaller than image. 805 806 for 0 <= y < self.surface.h: 807 row = <Uint32*> (pixels + self.surface.pitch * y) 808 809 for 0 <= x < self.surface.w: 810 811 if (row[x] & amask) >= amin: 812 813 if minx > x: 814 minx = x 815 if miny > y: 816 miny = y 817 if maxx < x: 818 maxx = x 819 if maxy < y: 820 maxy = y 821 822 self.unlock() 823 824 # Totally empty surface. 825 if minx > maxx: 826 return Rect((0, 0, 0, 0)) 827 828 x = minx 829 y = miny 830 w = min(maxx - minx + 1, self.surface.w - x) 831 h = min(maxy - miny + 1, self.surface.h - y) 832 833 return Rect((x, y, w, h)) 834 835 def get_view(self, kind='2'): 836 raise error("Surface.get_view is not supported.") 837 838 def get_buffer(self): 839 cdef Uint8 *pixels = <Uint8 *> self.surface.pixels 840 return pixels[self.surface.h * self.surface.pitch] 841 842 property _pixels_address: 843 def __get__(self): 844 return <Uint64> self.surface.pixels 845 846 847cdef api SDL_Surface *PySurface_AsSurface(surface): 848 return (<Surface> surface).surface 849 850cdef api object PySurface_New(SDL_Surface *surf): 851 cdef Surface rv = Surface(()) 852 rv.take_surface(surf) 853 return rv 854