0

I need to rotate a JPEG image 90 degrees, and unfortunately the file that results from System.Drawing.Image.RotateFlip is not compatible with my end program. As an alternative, therefore, I am trying to use Graphics.RotateTransform. The current code seems like it should work, but the image gets clipped when it is rotated.

This is the image that I am using for testing.

Here is the code I am using to rotate the image:

Public Shared Sub ProcessJpeg()
    Dim filePath As String = "C:\test.JPG"
    Dim img As Bitmap = Nothing
    img = New Bitmap(filePath)
    img = RotateImage(img, 90.0F)
    img.Save(Replace(filePath, ".JPG", "_new.JPG"))
End Sub

Shared Function RotateImage(b As Bitmap, angle As Single) As Bitmap
    'create a new empty bitmap to hold rotated image
    Dim returnBitmap As New Bitmap(b.Width, b.Height)
    'make a graphics object from the empty bitmap
    Dim g As Graphics = Graphics.FromImage(returnBitmap)
    'move rotation point to center of image
    g.TranslateTransform(CSng(b.Width) / 2, CSng(b.Height) / 2)
    'rotate
    g.RotateTransform(angle)
    'move image back
    g.TranslateTransform(-CSng(b.Width) / 2, -CSng(b.Height) / 2)
    'draw passed in image onto graphics object
    g.DrawImage(b, New Rectangle(New Point(0, 0), New Size(b.Width, b.Height)))
    Return returnBitmap
End Function

The RotateImage function is from here.

I thought the solution was to simply reverse b.Width and b.Height on the third-to-last line of the RotateImage function, but that just makes matters worse.

Alternatively, if there is a better way to rotate a JPEG (apart from System.Drawing.Image.RotateFlip), I'd love to hear about it.

cadsharp
  • 397
  • 3
  • 4
  • 16
  • 1
    What does `not compatible with my end program` mean? – LarsTech Apr 24 '14 at 18:48
  • My software exports JPEG images of CAD models and rotates the image 90 degrees (per customer instructions). The customer then imports the JPEG into a separate ERP program, which puts the JPEG in a PDF report. For some bizarre reason that has us all stumped, the images do not show up in the PDFs if they have been rotated using System.Drawing.RotateFlip. So it is my task to figure out another way to rotate the images. – cadsharp Apr 24 '14 at 19:02
  • Just a note: your save method isn't specifying the jpeg format. – LarsTech Apr 24 '14 at 19:06
  • I assume you are implying that .jpeg and .jpg are different file formats? If that is the case please correct me. In my research, I have read that .jpeg and .jpg are interchangeable. – cadsharp Apr 24 '14 at 19:16
  • No, you are just setting the file extension, not the actual format of the file. You would have to test it. Try `img.Save(Replace(filePath, ".JPG", "_new.JPG"), ImageFormat.Jpeg)` – LarsTech Apr 24 '14 at 19:21
  • Thanks for that insight. I will try that out with Image.RotateFlip. I would do cartwheels if that solved the entire problem. – cadsharp Apr 24 '14 at 19:42
  • 1
    You ended up being right, LarsTech. That was the problem. Thank you. – cadsharp May 08 '14 at 17:12

1 Answers1

3

In order to not clip the rotated image, you must make the target rectangle big enough to hold the rotation.

Take for instance a 10 by 5 sized rectangle. When it is rotated there are going to be angels that render an image that is bigger than 10x5. If you rotate by 90 then the dimensions are just flipped.

A method that I have used before was to: 1. Create a square that would hold the biggest size.
(A 10x5 would need a square of approximately 12x12.) 2. Rotate the image in the center of the square. 3. Clip the new square image down to the actual size of the rotated image.

Note that the following code works for ANY angle.

This code will make a square the size you need.

Public Function SquareRectangle(ByVal Newr As System.Drawing.Rectangle) As System.Drawing.Rectangle
    Dim Biggest = Newr.Size.Height
    If Newr.Size.Width > Biggest Then
        Biggest = Newr.Size.Width
    End If
    SquareRectangle = New System.Drawing.Rectangle(0, 0, Biggest, Biggest)
End Function

Then - this code will place the image in the center of that square.

Public Function CenterBitmap(ByVal OrignalImage As System.Drawing.Bitmap, ByVal SqsR As System.Drawing.Rectangle) As System.Drawing.Bitmap
    CenterBitmap = New System.Drawing.Bitmap(SqsR.Size.Width, SqsR.Size.Height)
    Dim WPadding = SqsR.Size.Width - OrignalImage.Size.Width
    Dim HPadding = SqsR.Size.Height - OrignalImage.Size.Height
    Dim Graphics As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(CenterBitmap)
    Dim PlaceR As New System.Drawing.Rectangle(CInt(WPadding / 2), CInt(HPadding / 2), OrignalImage.Size.Width, OrignalImage.Size.Height)
    Graphics.PageUnit = System.Drawing.GraphicsUnit.Pixel
    Graphics.DrawImage(OrignalImage, PlaceR)
End Function

Then - after rotating the square - this code will figure out the actual size of the rotated bitmap within that square.

Public Function BoundingBox(ByRef Dimentions As System.Drawing.Size, ByVal Degrees As Single) As System.Drawing.Rectangle
    ' determine the size needed to hold a rotated rectangle
    Dim Rad As Single = CSng((Degrees * Math.PI) / 180)

    Dim HalfX As Single = CSng(Dimentions.Width / 2)
    Dim HalfY As Single = CSng(Dimentions.Height / 2)
    Dim SinRad As Single = CSng(Math.Sin(Rad))
    Dim CosRad As Single = CSng(Math.Cos(Rad))
    Dim HalfXSinRad As Single = HalfX * SinRad
    Dim HalfXCosRad As Single = HalfX * CosRad
    Dim HalfYSinRad As Single = HalfY * SinRad
    Dim HalfYCosRad As Single = HalfY * CosRad

    Dim pXSpYC = HalfXSinRad + HalfYCosRad
    Dim pXSnYC = HalfXSinRad + -HalfYCosRad
    Dim pXCpYS = HalfXCosRad + HalfYSinRad
    Dim pXCnYS = HalfXCosRad + -HalfYSinRad
    Dim nXSpYC = -HalfXSinRad + HalfYCosRad
    Dim nXSnYC = -HalfXSinRad + -HalfYCosRad
    Dim nXCpYS = -HalfXCosRad + HalfYSinRad
    Dim nXCnYS = -HalfXCosRad + -HalfYSinRad

    Dim x_min = Math.Min(Math.Min(nXCpYS, pXCpYS), Math.Min(pXCnYS, nXCnYS))
    Dim x_max = Math.Max(Math.Max(nXCpYS, pXCpYS), Math.Max(pXCnYS, nXCnYS))

    Dim y_min = Math.Min(Math.Min(pXSpYC, nXSpYC), Math.Min(nXSnYC, pXSnYC))
    Dim y_max = Math.Max(Math.Max(pXSpYC, nXSpYC), Math.Max(nXSnYC, pXSnYC))

    BoundingBox = New System.Drawing.Rectangle
    BoundingBox.Height = CInt(y_max - y_min)
    BoundingBox.Width = CInt(x_max - x_min)

End Function
Scott Savage
  • 373
  • 1
  • 4
  • 17