Created
March 15, 2021 20:01
-
-
Save kmatch98/27bc30076e015e8acd0ec7bae1af9c7a to your computer and use it in GitHub Desktop.
Blit rotate scale in python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# /* | |
# Reference: | |
# "Fast Bitmap Rotation and Scaling" By Steven Mortimer, Dr Dobbs' Journal, July 01, 2001 | |
# http://www.drdobbs.com/architecture-and-design/fast-bitmap-rotation-and-scaling/184416337 | |
# See also http://www.efg2.com/Lab/ImageProcessing/RotateScanline.htm | |
# */ | |
# pylint: disable=invalid-name, too-many-branches, too-many-statements | |
# This function is provided in case the bitmaptools.rotozoom function is not available | |
def _blit_rotate_scale( | |
destination, # destination bitmap | |
ox=None, | |
oy=None, # (ox, oy) is the destination point where the source (px,py) is placed | |
dest_clip0=None, | |
dest_clip1=None, # clip0,1 is (x,y) corners of clip window on the destination bitmap | |
source=None, # source bitmap | |
px=None, | |
py=None, # (px, py) is the rotation point of the source bitmap | |
source_clip0=None, | |
source_clip1=None, # clip0,1 is (x,y) corners of clip window on the source bitmap | |
angle=0, # in radians, clockwise | |
scale=1.0, # scale factor (float) | |
skip_index=None, # color index to ignore | |
): | |
if source is None: | |
pass | |
# Check the input limits | |
if ox is None: | |
ox = destination.width / 2 | |
if oy is None: | |
oy = destination.height / 2 | |
if px is None: | |
px = source.width / 2 | |
if py is None: | |
py = source.height / 2 | |
if dest_clip0 is None: | |
dest_clip0 = (0, 0) | |
if dest_clip1 is None: | |
dest_clip1 = (destination.width, destination.height) | |
if source_clip0 is None: | |
source_clip0 = (0, 0) | |
if source_clip1 is None: | |
source_clip1 = (source.width, source.height) | |
minx = dest_clip1[0] | |
miny = dest_clip1[1] | |
maxx = dest_clip0[0] | |
maxy = dest_clip0[1] | |
sinAngle = math.sin(angle) | |
cosAngle = math.cos(angle) | |
dx = -cosAngle * px * scale + sinAngle * py * scale + ox | |
dy = -sinAngle * px * scale - cosAngle * py * scale + oy | |
if dx < minx: | |
minx = int(round(dx)) | |
if dx > maxx: | |
maxx = int(round(dx)) | |
if dy < miny: | |
miny = int(round(dy)) | |
if dy > maxy: | |
maxy = int(dy) | |
dx = cosAngle * (source.width - px) * scale + sinAngle * py * scale + ox | |
dy = sinAngle * (source.width - px) * scale - cosAngle * py * scale + oy | |
if dx < minx: | |
minx = int(round(dx)) | |
if dx > maxx: | |
maxx = int(round(dx)) | |
if dy < miny: | |
miny = int(round(dy)) | |
if dy > maxy: | |
maxy = int(round(dy)) | |
dx = ( | |
cosAngle * (source.width - px) * scale | |
- sinAngle * (source.height - py) * scale | |
+ ox | |
) | |
dy = ( | |
sinAngle * (source.width - px) * scale | |
+ cosAngle * (source.height - py) * scale | |
+ oy | |
) | |
if dx < minx: | |
minx = int(round(dx)) | |
if dx > maxx: | |
maxx = int(round(dx)) | |
if dy < miny: | |
miny = int(round(dy)) | |
if dy > maxy: | |
maxy = int(round(dy)) | |
dx = -cosAngle * px * scale - sinAngle * (source.height - py) * scale + ox | |
dy = -sinAngle * px * scale + cosAngle * (source.height - py) * scale + oy | |
if dx < minx: | |
minx = int(round(dx)) | |
if dx > maxx: | |
maxx = int(round(dx)) | |
if dy < miny: | |
miny = int(round(dy)) | |
if dy > maxy: | |
maxy = int(round(dy)) | |
# /* Clipping */ | |
if minx < dest_clip0[0]: | |
minx = dest_clip0[0] | |
if maxx > dest_clip1[0] - 1: | |
maxx = dest_clip1[0] - 1 | |
if miny < dest_clip0[1]: | |
miny = dest_clip0[1] | |
if maxy > dest_clip1[1] - 1: | |
maxy = dest_clip1[1] - 1 | |
dvCol = math.cos(angle) / scale | |
duCol = math.sin(angle) / scale | |
duRow = dvCol | |
dvRow = -duCol | |
startu = px - (ox * dvCol + oy * duCol) | |
startv = py - (ox * dvRow + oy * duRow) | |
rowu = startu + miny * duCol | |
rowv = startv + miny * dvCol | |
for y in range(miny, maxy + 1): # (y = miny, y <= maxy, y++) | |
u = rowu + minx * duRow | |
v = rowv + minx * dvRow | |
for x in range(minx, maxx + 1): # (x = minx, x <= maxx, x++) | |
if (source_clip0[0] <= u < source_clip1[0]) and ( | |
source_clip0[1] <= v < source_clip1[1] | |
): | |
# get the pixel color (c) from the source bitmap at (u,v) | |
c = source[ | |
int(u) + source.width * int(v) | |
] # direct index into bitmap is faster than tuple | |
# c = source[int(u), int(v)] | |
if c != skip_index: # ignore any pixels with skip_index | |
# place the pixel color (c) into the destination bitmap at (x,y) | |
destination[ | |
x + y * destination.width | |
] = c # direct index into bitmap is faster than tuple | |
# destination[x,y] = c | |
u += duRow | |
v += dvRow | |
rowu += duCol | |
rowv += dvCol |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment