Anonymous avatar Anonymous committed 651fb9d

rotozoom, chop and crop (whoops!) implemented.

Comments (0)

Files changed (5)

pygame/transform.py

 from SDL import *
 
 import pygame.base
+import pygame.rect
 import pygame.surface
 
 try:
     to hold the new size. If the image has pixel alphas, the padded area will
     be transparent. Otherwise pygame will pick a color that matches the Surface
     colorkey or the topleft pixel value.
+
+    :note: This function will perform slowly under certain conditions; see
+        `rotozoom`.
     
     :Parameters:
         `surface` : `Surface`
 
     :rtype: `Surface`
     '''
+    return rotozoom(surface, angle, 1.0)
+
+def rotozoom(surface, angle, scale):
+    '''Filtered scale and rotation.
+
+    This is a combined scale and rotation transform. The resulting Surface will
+    be a filtered 32-bit Surface. The scale argument is a floating point value
+    that will be multiplied by the current resolution. The angle argument is
+    a floating point value that represents the counterclockwise degrees to
+    rotate. A negative rotation angle will rotate clockwise.
+
+    Python Imaging Library (PIL) is required.  This function (and `rotate`)
+    will perform poorly unless one of the following conditions on the surface
+    is met:
+
+    * The surface has an alpha channel.
+    * The surface is not palettized and the background color is black.
+    * The surface is palettized and the background color is at index 0.
+    
+    :Parameters:
+        `surface` : `Surface`
+            Surface to transform.
+        `angle` : float
+            Degrees to rotate anticlockwise.
+        `scale` : float
+            Scale to apply to surface size.
+    '''
     if not _have_PIL:
         raise NotImplementedError, 'Python imaging library (PIL) required.'
 
+    if surface._surf.format.BytesPerPixel == 3:
+        raise NotImplementedError, 'TODO: 24-bit RGB'
+
     # XXX: Differ from Pygame: subsurfaces permitted.
     surf = surface._surf
 
     radians = angle * (math.pi / 180.0)
     sa = math.sin(radians)
     ca = math.cos(radians)
-    cx = ca * surf.w
-    cy = ca * surf.h
-    sx = sa * surf.w
-    sy = sa * surf.h
+    cx = ca * surf.w * scale
+    cy = ca * surf.h * scale
+    sx = sa * surf.w * scale
+    sy = sa * surf.h * scale
     width = int(max(abs(cx + sy), abs(cx - sy), abs(-cx + sy), abs(-cx - sy)))
     height = int(max(abs(sx + cy), abs(sx - cy), abs(-sx + cy), abs(-sx - cy)))
     newsurf = _newsurf_fromsurf(surf, width, height)
+    sa /= scale
+    ca /= scale
 
     surface._prep()
     if surf.flags & SDL_SRCCOLORKEY:
     _from_PIL(image, newsurf)
 
     if need_data_sub:
+        # Swap [0, 0, 0, 0] back with the background color.
         SDL_LockSurface(newsurf)
         data = newsurf.pixels.to_string()
-        '''
-        def repl(match):
-            if match.group(0) == '\000\000\000\000':
-                return background_bytes
-            elif match.group(0) == background_bytes:
-                return '\000\000\000\000'
-            else:
-                return match.group(0)
-        '''
-        pattern = re.compile('....', re.DOTALL)
         data = pattern.sub(repl, data)
         memmove(newsurf.pixels.ptr, data, len(data))
         SDL_UnlockSurface(newsurf)
 
     return pygame.surface.Surface(surf=newsurf)
 
-def rotozoom(surface, angle, scale):
-    '''Filtered scale and rotation.
-
-    This is a combined scale and rotation transform. The resulting Surface will
-    be a filtered 32-bit Surface. The scale argument is a floating point value
-    that will be multiplied by the current resolution. The angle argument is
-    a floating point value that represents the counterclockwise degrees to
-    rotate. A negative rotation angle will rotate clockwise.
-    
-    :Parameters:
-        `surface` : `Surface`
-            Surface to transform.
-        `angle` : float
-            Degrees to rotate anticlockwise.
-        `scale` : float
-            Scale to apply to surface size.
-    '''
-
 def scale2x(surface, dest=None):
     '''Specialized image doubler.
 
     else:
         return pygame.surface.Surface(surf=newsurf)
 
+# What the hell is this used for?
 def chop(surface, rect):
+    '''Remove interior area of an image.
+
+    Extracts a portion of an image. All vertical and
+    horizontal pixels surrounding the given rectangle area are removed (a
+    cross shape). The resulting image is shrunken by the size of pixels
+    removed.  (The original image is not altered by this operation.)
+
+    :Parameters:
+        `surface` : `Surface`
+            Surface to chop.
+        `rect` : `Rect`
+            Area to remove.
+
+    :rtype: `Surface`
+    '''
+    rect = pygame.rect._rect_from_object(rect)._r
+    surf = surface._surf
+
+    rect.x = max(0, rect.x)
+    rect.y = max(0, rect.y)
+    rect.w = min(surf.w, rect.w + rect.x) - rect.x
+    rect.h = min(surf.h, rect.h + rect.y) - rect.y
+
+    newsurf = _newsurf_fromsurf(surf, surf.w - rect.w, surf.h - rect.h)
+
+    surface.lock()
+    data = surf.pixels.to_string()
+    surface.unlock()
+
+    # Chop Y
+    pitch = surf.pitch
+    data = data[:rect.y*pitch] + data[rect.y*pitch+rect.h*pitch:]
+
+    # Chop X and pitch correction
+    rows = re.findall('.' * surf.pitch, data, re.DOTALL)
+    x1 = rect.x * newsurf.format.BytesPerPixel
+    x2 = (rect.x + rect.w) * newsurf.format.BytesPerPixel
+    pad = ''
+    padlength = newsurf.pitch - newsurf.w * newsurf.format.BytesPerPixel
+    if padlength > 0:
+        pad = '\0' * padlength
+    for i in range(len(rows)):
+        rows[i] = rows[i][:x1] + rows[i][x2:] + pad
+    data = ''.join(rows) 
+
+    SDL_LockSurface(newsurf)
+    memmove(newsurf.pixels.ptr, data, len(data))
+    SDL_UnlockSurface(newsurf)
+
+    return pygame.surface.Surface(surf=newsurf)
+
+# Implemented this by mistake... misread the chop() documentation :-)
+def crop(surface, rect):
     '''Extract a rectangular area of an image.
 
     Extracts a portion of an image. All vertical and
         `surface` : `Surface`
             Surface to crop.
         `rect` : `Rect`
-            Area to extract.
+            Area to keep.
 
     :rtype: `Surface`
     '''
+    rect = pygame.rect._rect_from_object(rect)._r
+    surf = surface._surf
+
+    rect.x = max(0, rect.x)
+    rect.y = max(0, rect.y)
+    rect.w = min(surf.w, rect.w + rect.x) - rect.x
+    rect.h = min(surf.h, rect.h + rect.y) - rect.y
+
+    newsurf = _newsurf_fromsurf(surf, rect.w, rect.h)
+
+    surface.lock()
+    data = surf.pixels.to_string()
+    surface.unlock()
+
+    # Crop Y
+    pitch = surf.pitch
+    data = data[rect.y*pitch:rect.y*pitch+rect.h*pitch]
+
+    # Crop X and pitch correction
+    rows = re.findall('.' * surf.pitch, data, re.DOTALL)
+    x1 = rect.x * newsurf.format.BytesPerPixel
+    x2 = (rect.x + rect.w) * newsurf.format.BytesPerPixel
+    pad = ''
+    padlength = newsurf.pitch - newsurf.w * newsurf.format.BytesPerPixel
+    if padlength > 0:
+        pad = '\0' * padlength
+    for i in range(len(rows)):
+        rows[i] = rows[i][x1:x2] + pad
+    data = ''.join(rows) 
+
+    SDL_LockSurface(newsurf)
+    memmove(newsurf.pixels.ptr, data, len(data))
+    SDL_UnlockSurface(newsurf)
+
+    return pygame.surface.Surface(surf=newsurf)

test_pygame/transform_chop.py

+#!/usr/bin/env python
+
+'''Check that chop works.
+'''
+
+__docformat__ = 'restructuredtext'
+__version__ = '$Id$'
+
+import math
+import os
+import sys
+
+import pygame
+from pygame.locals import *
+
+if __name__ == '__main__':
+    pygame.init()
+
+    if len(sys.argv) > 1:
+        image_file = sys.argv[1]
+    else:
+        image_file = os.path.join(os.path.dirname(sys.argv[0]), 
+                                  '../test/sample.bmp')
+
+    image = pygame.image.load(image_file)
+    w, h = image.get_size()
+
+
+    dx = 1
+    dy = 1
+    x = 0
+    y = 0
+    chop_w = w / 4
+    chop_h = h / 4
+    screen = pygame.display.set_mode((w - chop_w, h - chop_h))
+    clock = pygame.time.Clock()
+    while True:
+        for event in pygame.event.get():
+            if event.type in (KEYDOWN, QUIT):
+                sys.exit(0)
+
+        clock.tick(60)
+        x += dx
+        y += dy
+        if x + chop_w >= w or x < 0:
+            dx = -dx
+        if y + chop_h >= h or y < 0:
+            dy = -dy
+
+        surf = pygame.transform.chop(image, (x, y, chop_w, chop_h))
+
+        screen.fill((255, 0, 0))
+        screen.blit(surf, (0, 0))
+        pygame.display.flip()
+

test_pygame/transform_crop.py

+#!/usr/bin/env python
+
+'''Check that crop works.
+'''
+
+__docformat__ = 'restructuredtext'
+__version__ = '$Id$'
+
+import math
+import os
+import sys
+
+import pygame
+from pygame.locals import *
+
+if __name__ == '__main__':
+    pygame.init()
+
+    if len(sys.argv) > 1:
+        image_file = sys.argv[1]
+    else:
+        image_file = os.path.join(os.path.dirname(sys.argv[0]), 
+                                  '../test/sample.bmp')
+
+    image = pygame.image.load(image_file)
+    w, h = image.get_size()
+
+    screen = pygame.display.set_mode((w, h))
+
+    dx = 1
+    dy = 1
+    x = 0
+    y = 0
+    view_w = w / 3
+    view_h = h / 3
+    clock = pygame.time.Clock()
+    while True:
+        for event in pygame.event.get():
+            if event.type in (KEYDOWN, QUIT):
+                sys.exit(0)
+
+        clock.tick(60)
+        x += dx
+        y += dy
+        if x + view_w >= w or x < 0:
+            dx = -dx
+        if y + view_h >= h or y < 0:
+            dy = -dy
+
+        surf = pygame.transform.crop(image, (x, y, view_w, view_h))
+
+        screen.fill((255, 0, 0))
+        screen.blit(surf, (x, y))
+        pygame.display.flip()
+

test_pygame/transform_rotate.py

+#!/usr/bin/env python
+
+'''Check that rotate works.
+'''
+
+__docformat__ = 'restructuredtext'
+__version__ = '$Id$'
+
+import math
+import os
+import sys
+
+import pygame
+from pygame.locals import *
+import Image
+
+if __name__ == '__main__':
+    pygame.init()
+
+    if len(sys.argv) > 1:
+        image_file = sys.argv[1]
+    else:
+        image_file = os.path.join(os.path.dirname(sys.argv[0]), 
+                                  '../test/sample.bmp')
+
+    image = pygame.image.load(image_file)
+    image.set_colorkey(image.get_at((0, 0)))
+    #image.set_colorkey((0, 0, 0))
+    w, h = image.get_size()
+
+    width = height = int(math.sqrt(w ** 2 + h ** 2))
+    screen = pygame.display.set_mode((width, height))
+
+    angle = 0
+    clock = pygame.time.Clock()
+    while True:
+        for event in pygame.event.get():
+            if event.type in (KEYDOWN, QUIT):
+                sys.exit(0)
+
+        clock.tick(60)
+        angle += 1  
+
+        surf = pygame.transform.rotate(image, angle)
+
+        w, h = surf.get_size()
+        screen.fill((255, 0, 0))
+        screen.blit(surf, (width / 2 - w / 2, height / 2 - h / 2))
+        pygame.display.flip()
+

test_pygame/transform_rotozoom.py

+#!/usr/bin/env python
+
+'''Check that rotate works.
+'''
+
+__docformat__ = 'restructuredtext'
+__version__ = '$Id$'
+
+import math
+import os
+import sys
+
+import pygame
+from pygame.locals import *
+import Image
+
+if __name__ == '__main__':
+    pygame.init()
+
+    if len(sys.argv) > 1:
+        image_file = sys.argv[1]
+    else:
+        image_file = os.path.join(os.path.dirname(sys.argv[0]), 
+                                  '../test/sample.bmp')
+
+    image = pygame.image.load(image_file)
+    image.set_colorkey(image.get_at((0, 0)))
+    w, h = image.get_size()
+
+    width = height = int(math.sqrt(w ** 2 + h ** 2))
+    screen = pygame.display.set_mode((width, height))
+
+    angle = 0
+    z = 0.1
+    dz = 0.01
+    clock = pygame.time.Clock()
+    while True:
+        for event in pygame.event.get():
+            if event.type in (KEYDOWN, QUIT):
+                sys.exit(0)
+
+        #clock.tick(60)
+        angle += 3  
+        z += dz
+        if z > 2.0 or z < 0.1:
+            dz = -dz
+
+        surf = pygame.transform.rotozoom(image, angle, z)
+
+        w, h = surf.get_size()
+        screen.fill((255, 0, 0))
+        screen.blit(surf, (width / 2 - w / 2, height / 2 - h / 2))
+        pygame.display.flip()
+
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.