def rotate(self, degrees, clockwise=False, resample_algorithm=nearest, resize_canvas=True): """ Returns the image obtained by rotating this image by the given number of degrees. Anticlockwise unless clockwise=True is given. """ # translate to the origin first, then rotate, then translate back transform = AffineTransform() transform = transform.translate(self.width * -0.5, self.height * -0.5) transform = transform.rotate(degrees, clockwise=clockwise) width, height = self.width, self.height if resize_canvas: # determine new width width, height = get_transformed_dimensions(transform, (0, 0, width, height)) transform = transform.translate(width * 0.5, height * 0.5) pixels = resample_algorithm.affine(self, transform, resize_canvas=resize_canvas) return self._copy(pixels)
def affine(self, source, transform, resize_canvas=True): if resize_canvas: # get image dimensions width, height = get_transformed_dimensions( transform, (0, 0, source.width, source.height) ) else: width = source.width height = source.height pixelsize = source.pixelsize # transparent or black background background = [0] * pixelsize # we want to go from dest coords to src coords: transform = transform.inverse() # Optimisation: # Because affine transforms have no perspective component, # the *gradient* of each source row/column must be constant. # So, we can calculate the source coordinates for each corner, # and then interpolate for each pixel, instead of doing a # matrix multiplication for each pixel. x_range = range(width) y_range = range(height) new_array = source.pixels.copy() for y in y_range: line = array.array('B') # initialize a new line # the 0.5's mean we use the center of each pixel row_x0, row_y0 = transform * (0.5, y + 0.5) row_x1, row_y1 = transform * (width + 0.5, y + 0.5) dx = float(row_x1 - row_x0) / source.width dy = float(row_y1 - row_y0) / source.width for x in x_range: source_x = int(row_x0 + dx * x) source_y = int(row_y0 + dy * x) new_array.set(x, y, self._get_value(source, source_x, source_y, dx, dy) or background ) return new_array
def affine(self, source, transform, resize_canvas=True): if resize_canvas: # get image dimensions width, height = get_transformed_dimensions( transform, (0, 0, source.width, source.height) ) else: width = source.width height = source.height pixelsize = source.pixelsize # transparent or black background background = [0] * pixelsize # we want to go from dest coords to src coords: transform = transform.inverse() # Optimisation: # Because affine transforms have no perspective component, # the *gradient* of each source row/column must be constant. # So, we can calculate the source coordinates for each corner, # and then interpolate for each pixel, instead of doing a # matrix multiplication for each pixel. x_range = range(width) y_range = range(height) new_array = source.pixels.copy() for y in y_range: # the 0.5's mean we use the center of each pixel row_x0, row_y0 = transform * (0.5, y + 0.5) row_x1, row_y1 = transform * (width + 0.5, y + 0.5) dx = float(row_x1 - row_x0) / source.width dy = float(row_y1 - row_y0) / source.width for x in x_range: source_x = int(row_x0 + dx * x) source_y = int(row_y0 + dy * x) new_array.set(x, y, self._get_value(source, source_x, source_y, dx, dy) or background ) return new_array