I think this functionality could be added in _get_inverse_affine_matrix by providing the shear argument as a list or tuple.
These would probably be the necessary changes:
def _get_inverse_affine_matrix(center, angle, translate, scale, shear):
# Helper method to compute inverse matrix for affine transformation
# As it is explained in PIL.Image.rotate
# We need compute INVERSE of affine transformation matrix: M = T * C * RSS * C^-1
# where T is translation matrix: [1, 0, tx | 0, 1, ty | 0, 0, 1]
# C is translation matrix to keep center: [1, 0, cx | 0, 1, cy | 0, 0, 1]
# RSS is rotation with scale and shear matrix
# RSS(a, scale, shear) = [ cos(a)*scale -sin(a + shear)*scale 0]
# [ sin(a)*scale cos(a + shear)*scale 0]
# [ 0 0 1]
# Thus, the inverse is M^-1 = C * RSS^-1 * C^-1 * T^-1
if isinstance(shear, (tuple, list)) and len(shear) == 2:
shear[0] = math.radians(shear[0])
shear[1] = math.radians(shear[1])
else:
shear = math.radians(shear)
shear = [shear, shear]
angle = math.radians(angle)
scale = 1.0 / scale
# Inverted rotation matrix with scale and shear
d = math.cos(angle + shear[0]) * math.cos(angle + shear[1]) + math.sin(angle + shear[0]) * math.sin(angle + shear[1])
matrix = [
math.cos(angle + shear[0]), math.sin(angle + shear[0]), 0,
-math.sin(angle + shear[1]), math.cos(angle + shear[1]), 0
]
matrix = [scale / d * m for m in matrix]
# Apply inverse of translation and of center translation: RSS^-1 * C^-1 * T^-1
matrix[2] += matrix[0] * (-center[0] - translate[0]) + matrix[1] * (-center[1] - translate[1])
matrix[5] += matrix[3] * (-center[0] - translate[0]) + matrix[4] * (-center[1] - translate[1])
# Apply center translation: C * RSS^-1 * C^-1 * T^-1
matrix[2] += center[0]
matrix[5] += center[1]
return matrix
CC @fmassa
Let me know, if that looks good and I could create a PR for it.