<p>as paulHK said:</p>
<blockquote>
<p>One fundamental issue here is you're rotating in destination space rather than source space (e.g. apply the shear on source coordinates and output a rectangle). This way you can guarantee every pixel in the output is drawn once and only once and there are no gaps in between.</p>
</blockquote>
<p>and the line that's causing the 180 problem is this line:</p>
<p><code>tangent=math.tan(angle/2)</code></p>
<p>If the angle is 180, the tan of 90 will be basically infinity.</p>
<p>This is a graph of 1 / (cos(rotation) ^ 2), one of the shearing calculations the narrator at datagenetics.com uses:
<a href="https://i.sstatic.net/c2roH.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/c2roH.png" alt="graph showing extreme peaks of function" /></a></p>
<p>That means his method stalls at certain angles.</p>
<p>In all the code i wrote, I never needed to call tan(x).
Use cos(x) and sin(x) instead, also use as few divisions as possible that could create infinity. Most rendering engines tackle this problem by limiting the angle of the camera to 89 degrees.</p>
<p>To get unaliased rotated images, i suggest a better drawing method, which computer graphics use today:</p>
<p>Use a 3x3 <a href="https://en.wikipedia.org/wiki/Rotation_matrix" rel="nofollow noreferrer">rotation matrix</a> and a <a href="https://en.wikipedia.org/wiki/Translation_(geometry)#Matrix_representation" rel="nofollow noreferrer">translation matrix</a> , and loop over the pixels image that you want to draw on:</p>
<p>cos(angle) -sin(angle) + offset x <br>
sin(angle) cos(angle)  + offset y <br>
0          0           1 <br></p>
<p>to get this matrix, multiply first: <br></p>
<p>1 0 -[rotation centre x]  <br>
0 1 -[rotation centre y]  <br>
0 0 1 <br></p>
<p>then: <br></p>
<p>cos(angle) -sin(angle) 0 <br>
sin(angle) cos(angle)  0 <br>
0          0           1</p>
<p>and then: <br>
1 0 [rotation centre x]  <br>
0 1 [rotation centre y]  <br>
0 0 1 <br></p>
<p>and then(if needed to translate): <br>
1 0 [translation x]  <br>
0 1 [translation y]  <br>
0 0 1 <br></p>
<p>Multiply the position on the target image by the <strong>INVERSE</strong> of the combined matrix to get the position on the source image.</p>
<p>If the position is in the bounds of the source image,
fill the target pixel with the source pixel at that location.</p>

CC BY-SA 4.0
2021-01-28T22:46:41.223Z
2021-01-28T22:46:41.223Z
John Heikens
<p>I am rotating a bitmap using the three shear algorithm documented in these articles [<a href="https://datagenetics.com/blog/august32013/index.html" rel="nofollow noreferrer">1</a>][<a href="https://medium.com/@gautamnagrawal/rotating-image-by-any-angle-shear-transformation-using-only-numpy-d28d16eb5076" rel="nofollow noreferrer">2</a>].</p>
<p>From about 0-90°, the quality is acceptable, but beyond that it gets progressively more distorted until it's unintelligible.
<a href="https://i.sstatic.net/eKfDz.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/eKfDz.png" alt="Bitmap Image Rotations" /></a></p>
<p>Can anyone help me locate what is going wrong? Thank you!</p>
<p>This is my shear function:</p>
<pre class="lang-python prettyprint-override"><code>def shear(angle,x,y):
    '''
    |1  -tan(/2) |  |1        0|  |1  -tan(/2) |
    |0      1     |  |sin()   1|  |0      1     |

    '''
    # shear 1
    tangent=math.tan(angle/2)
    new_x=round(x-y*tangent)
    new_y=y

    #shear 2
    new_y=round(new_x*math.sin(angle)+new_y)      #since there is no change in new_x according to the shear matrix

    #shear 3
    new_x=round(new_x-new_y*tangent)              #since there is no change in new_y according to the shear matrix

    return new_x,new_y
</code></pre>
<p>This is the code in the draw function:</p>
<pre class="lang-python prettyprint-override"><code>cos = math.cos(self.rotation)
sin = math.sin(self.rotation)

# Define the width and height of the destination image
newWidth = round(abs(w*cos)+abs(h*sin))+1
newHeight = round(abs(h*cos)+abs(w*sin))+1

# Find the center of the source image for rotation
origCenterWidth  = round(((w+1)/2)-1)    #with respect to the source image
origCenterHeight = round(((h+1)/2)-1)    #with respect to the source image

# Find the center of the destination image
newCenterWidth  = round(((newWidth+1)/2)-1)  #with respect to the destination image
newCenterHeight = round(((newHeight)/2)-1)  #with respect to the destination image

for i in range(h):
    for j in range(w):
        #co-ordinates of pixel with respect to the center of source image
        x = w-1-j-origCenterWidth
        y = h-1-i-origCenterHeight

        #Applying the Shear Transformation
        new_x,new_y = shear(self.rotation,x,y)

        #with rotation, the center will change so new_x and new_y will be the new center
        new_y = newCenterHeight-new_y
        new_x = newCenterWidth-new_x

        alphaValue = sourceBmp.GetAlphaPixel(alphaChannel, j, i) #gets the source image pixel's alpha
        col = sourceBmp.GetPixelDirect(j, i) #gets the source image pixel's color as a Color Vector
        destBmp.SetAlphaPixel(nBmpAlpha, int(new_x), int(new_y), alphaValue) #sets the destination image pixel's alpha
        destBmp.SetPixel(int(new_x), int(new_y), int(col.x), int(col.y), int(col.z)) #sets the destination image pixel's color
</code></pre>

CC BY-SA 4.0
2021-01-28T07:24:51.557Z
One fundamental issue here is you're rotating in destination space rather than source space (e.g. apply the shear on source coordinates and output a rectangle). This way you can guarantee every pixel in the output is drawn once and only once and there are no gaps in between.
PaulHK
<transformations><algorithm><matrix><2d><antialiasing>
Rotate a Bitmap with Shearing
Dr. Pontchartrain
# Rotate a Bitmap with Shearing

(Asked by: username_0 on 2021-01-26)

I am rotating a bitmap using the three shear algorithm documented in these articles [[1](https://datagenetics.com/blog/august32013/index.html)][[2](https://medium.com/@gautamnagrawal/rotating-image-by-any-angle-shear-transformation-using-only-numpy-d28d16eb5076)].

From about 0-90°, the quality is acceptable, but beyond that it gets progressively more distorted until it's unintelligible.
[![Bitmap Image Rotations](https://i.sstatic.net/eKfDz.png)](https://i.sstatic.net/eKfDz.png)

Can anyone help me locate what is going wrong? Thank you!

This is my shear function:

```python
def shear(angle,x,y):
    '''
    |1  -tan(/2) |  |1        0|  |1  -tan(/2) |
    |0      1     |  |sin()   1|  |0      1     |

    '''
    # shear 1
    tangent=math.tan(angle/2)
    new_x=round(x-y*tangent)
    new_y=y

    #shear 2
    new_y=round(new_x*math.sin(angle)+new_y)      #since there is no change in new_x according to the shear matrix

    #shear 3
    new_x=round(new_x-new_y*tangent)              #since there is no change in new_y according to the shear matrix

    return new_x,new_y

```

This is the code in the draw function:

```python
cos = math.cos(self.rotation)
sin = math.sin(self.rotation)

# Define the width and height of the destination image
newWidth = round(abs(w*cos)+abs(h*sin))+1
newHeight = round(abs(h*cos)+abs(w*sin))+1

# Find the center of the source image for rotation
origCenterWidth  = round(((w+1)/2)-1)    #with respect to the source image
origCenterHeight = round(((h+1)/2)-1)    #with respect to the source image

# Find the center of the destination image
newCenterWidth  = round(((newWidth+1)/2)-1)  #with respect to the destination image
newCenterHeight = round(((newHeight)/2)-1)  #with respect to the destination image

for i in range(h):
    for j in range(w):
        #co-ordinates of pixel with respect to the center of source image
        x = w-1-j-origCenterWidth
        y = h-1-i-origCenterHeight

        #Applying the Shear Transformation
        new_x,new_y = shear(self.rotation,x,y)

        #with rotation, the center will change so new_x and new_y will be the new center
        new_y = newCenterHeight-new_y
        new_x = newCenterWidth-new_x

        alphaValue = sourceBmp.GetAlphaPixel(alphaChannel, j, i) #gets the source image pixel's alpha
        col = sourceBmp.GetPixelDirect(j, i) #gets the source image pixel's color as a Color Vector
        destBmp.SetAlphaPixel(nBmpAlpha, int(new_x), int(new_y), alphaValue) #sets the destination image pixel's alpha
        destBmp.SetPixel(int(new_x), int(new_y), int(col.x), int(col.y), int(col.z)) #sets the destination image pixel's color

```

**Tags:** transformations, algorithm, matrix, 2d, antialiasing

> **Comments:**
> **username_1**: One fundamental issue here is you're rotating in destination space rather than source space (e.g. apply the shear on source coordinates and output a rectangle). This way you can guarantee every pixel in the output is drawn once and only once and there are no gaps in between.  

---

## 1 answers

### Answer by username_2 on 2021-01-28. Score: 2

as paulHK said:

> One fundamental issue here is you're rotating in destination space rather than source space (e.g. apply the shear on source coordinates and output a rectangle). This way you can guarantee every pixel in the output is drawn once and only once and there are no gaps in between.

and the line that's causing the 180 problem is this line:

`tangent=math.tan(angle/2)`

If the angle is 180, the tan of 90 will be basically infinity.

This is a graph of 1 / (cos(rotation) ^ 2), one of the shearing calculations the narrator at datagenetics.com uses:
[![graph showing extreme peaks of function](https://i.sstatic.net/c2roH.png)](https://i.sstatic.net/c2roH.png)

That means his method stalls at certain angles.

In all the code i wrote, I never needed to call tan(x).
Use cos(x) and sin(x) instead, also use as few divisions as possible that could create infinity. Most rendering engines tackle this problem by limiting the angle of the camera to 89 degrees.

To get unaliased rotated images, i suggest a better drawing method, which computer graphics use today:

Use a 3x3 [rotation matrix](https://en.wikipedia.org/wiki/Rotation_matrix) and a [translation matrix](https://en.wikipedia.org/wiki/Translation_(geometry)#Matrix_representation) , and loop over the pixels image that you want to draw on:

cos(angle) -sin(angle) + offset x   
sin(angle) cos(angle) + offset y   
0 0 1

to get this matrix, multiply first:

1 0 -[rotation centre x]   
0 1 -[rotation centre y]   
0 0 1

then:

cos(angle) -sin(angle) 0   
sin(angle) cos(angle) 0   
0 0 1

and then:   
1 0 [rotation centre x]   
0 1 [rotation centre y]   
0 0 1

and then(if needed to translate):   
1 0 [translation x]   
0 1 [translation y]   
0 0 1

Multiply the position on the target image by the **INVERSE** of the combined matrix to get the position on the source image.

If the position is in the bounds of the source image,
fill the target pixel with the source pixel at that location.


---

