illume avatar illume committed 55d7ab7

Surface.fill() respects clipping rect or surface bounds with special flags.

Thanks rdeaton, Sagie Maoz, and qbradq. Fixes #101

Comments (0)

Files changed (3)

 int
 surface_fill_blend (SDL_Surface *surface, SDL_Rect *rect, Uint32 color,
                     int blendargs);
+                    
+void
+surface_respect_clip_rect (SDL_Surface *surface, SDL_Rect *rect);
 
 int 
 pygame_AlphaBlit (SDL_Surface * src, SDL_Rect * srcrect,

src/surface_fill.c

 #define NO_PYGAME_C_API
 #include "surface.h"
 
+/*
+ * Changes SDL_Rect to respect any clipping rect defined on the surface.
+ * Necessary when modifying surface->pixels directly instead of through an
+ * SDL interface.
+ */
+void surface_respect_clip_rect (SDL_Surface *surface, SDL_Rect *rect)
+{
+	SDL_Rect tmp;
+	SDL_Rect *A, *B;
+    int x, y, w, h;
+
+    A = rect;
+	B = &tmp;
+    SDL_GetClipRect(surface, B);
+	
+	/* Code here is nearly identical to rect_clip in rect.c */
+
+    /* Left */
+    if ((A->x >= B->x) && (A->x < (B->x + B->w)))
+        x = A->x;
+    else if ((B->x >= A->x) && (B->x < (A->x + A->w)))
+        x = B->x;
+    else
+        return;
+
+    /* Right */
+    if (((A->x + A->w) > B->x) && ((A->x + A->w) <= (B->x + B->w)))
+        w = (A->x + A->w) - x;
+    else if (((B->x + B->w) > A->x) && ((B->x + B->w) <= (A->x + A->w)))
+        w = (B->x + B->w) - x;
+    else
+        return;
+
+    /* Top */
+    if ((A->y >= B->y) && (A->y < (B->y + B->h)))
+        y = A->y;
+    else if ((B->y >= A->y) && (B->y < (A->y + A->h)))
+        y = B->y;
+    else
+        return;
+
+    /* Bottom */
+    if (((A->y + A->h) > B->y) && ((A->y + A->h) <= (B->y + B->h)))
+        h = (A->y + A->h) - y;
+    else if (((B->y + B->h) > A->y) && ((B->y + B->h) <= (A->y + A->h)))
+        h = (B->y + B->h) - y;
+    else
+        return;
+
+    rect->x = x;
+    rect->y = y;
+    rect->w = w;
+    rect->h = h;
+}
 
 static int
 surface_fill_blend_add (SDL_Surface *surface, SDL_Rect *rect, Uint32 color)
 {
     int result = -1;
     int locked = 0;
+    
+    surface_respect_clip_rect(surface, rect);
 
     /* Lock the surface, if needed */
     if (SDL_MUSTLOCK (surface))

test/surface_test.py

             d.blit(s, (0, 0))
         sub = surf.subsurface((1, 1, 2, 2))
         self.failUnlessRaises(pygame.error, do_blit, surf, sub)
-        
+
+
+class SurfaceFillTest(unittest.TestCase):
+    def test_fill(self):
+        pygame.init()
+        try:
+            screen = pygame.display.set_mode((640, 480))
+
+            # Green and blue test pattern
+            screen.fill((0, 255, 0), (0, 0, 320, 240))
+            screen.fill((0, 255, 0), (320, 240, 320, 240))
+            screen.fill((0, 0, 255), (320, 0, 320, 240))
+            screen.fill((0, 0, 255), (0, 240, 320, 240))
+
+            # Now apply a clip rect, such that only the left side of the
+            # screen should be effected by blit opperations.
+            screen.set_clip((0, 0, 320, 480))
+
+            # Test fills with each special flag, and additionaly without any.
+            screen.fill((255, 0, 0, 127), (160, 0, 320, 30), 0)
+            screen.fill((255, 0, 0, 127), (160, 30, 320, 30), pygame.BLEND_ADD)
+            screen.fill((0, 127, 127, 127), (160, 60, 320, 30), pygame.BLEND_SUB)
+            screen.fill((0, 63, 63, 127), (160, 90, 320, 30), pygame.BLEND_MULT)
+            screen.fill((0, 127, 127, 127), (160, 120, 320, 30), pygame.BLEND_MIN)
+            screen.fill((127, 0, 0, 127), (160, 150, 320, 30), pygame.BLEND_MAX)
+            screen.fill((255, 0, 0, 127), (160, 180, 320, 30), pygame.BLEND_RGBA_ADD)
+            screen.fill((0, 127, 127, 127), (160, 210, 320, 30), pygame.BLEND_RGBA_SUB)
+            screen.fill((0, 63, 63, 127), (160, 240, 320, 30), pygame.BLEND_RGBA_MULT)
+            screen.fill((0, 127, 127, 127), (160, 270, 320, 30), pygame.BLEND_RGBA_MIN)
+            screen.fill((127, 0, 0, 127), (160, 300, 320, 30), pygame.BLEND_RGBA_MAX)
+            screen.fill((255, 0, 0, 127), (160, 330, 320, 30), pygame.BLEND_RGB_ADD)
+            screen.fill((0, 127, 127, 127), (160, 360, 320, 30), pygame.BLEND_RGB_SUB)
+            screen.fill((0, 63, 63, 127), (160, 390, 320, 30), pygame.BLEND_RGB_MULT)
+            screen.fill((0, 127, 127, 127), (160, 420, 320, 30), pygame.BLEND_RGB_MIN)
+            screen.fill((255, 0, 0, 127), (160, 450, 320, 30), pygame.BLEND_RGB_MAX)
+
+            # Update the display so we can see the results
+            pygame.display.flip()
+
+            # Compare colors on both sides of window
+            y = 5
+            while y < 480:
+                self.assertEquals(screen.get_at((10, y)),
+                        screen.get_at((330, 480 - y)))
+                y += 10
+
+        finally:
+            pygame.quit()
 
 if __name__ == '__main__':
     unittest.main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.