Introduction
How many times have you wanted to rotate an image in a PictureBox
control? I know I've wanted to do it on many occasions.
This control makes it simple to rotate your images. Referencing it is easy in code - the namespace is System.Windows.Forms
so you can easily declare one as RImage
.
Properties/Methods
This control inherits everything so any property/method a PictureBox
has, this control will have as well - however, there are several properties that will allow you other options that were built into the control.
Public Property ShowThrough() As Boolean
Public Property Direction() As DirectionEnum
Public Property Rotation() As Integer
Public Property TransparentColor() As Color
ShowThrough
will determine whether or not space not used by the image will be transparent and show the control below it. This is accomplished using regions.
Direction
as you notice is declared as DirectionEnum
. This enumeration was created for the control. It tells the control which way to rotate the image: Clockwise
, or Counter_Clockwise
.
Rotation
is the angle of rotation of the image. It is in degrees. The range is 0 to 359; however, inputting a number outside of the range will scale it to the appropriate angle that is in the range.
TransparentColor
will allow you to set a transparent color for the image. The demo project has an image included with a Lime background. If the TransparentColor
property is set to Lime - that color in the image will be painted transparent. Unfortunately, it doesn't show the controls below it through, just the parent's background color.
Adding the Control
Right-click on an empty area of your toolbox and choose Add/Remove Items. At the dialog screen on the .NET Framework Components tab, choose Browse and navigate to the DLL file. Once you've done this, hit OK.
You've now added the control to the toolbox.
Using the Control
Using the control is simple. It works almost exactly like the PictureBox
control. Add one to your form, and give it an image. Once it has an image, ShowThrough
will start working (if there is no image, OnPaint
quits before it can set the transparent region) if it is set.
Now just set your Direction
from the drop-down box in the Properties window and enter an angle for your Rotation
.
If you'd like to set a transparent color for your image - you can do that with TransparentColor
in the Properties window.
How it works
All of the rotation is handled in the OnPaint
event. I start by getting the current corners of the image.
Dim bm_in As New Bitmap(MyBase.Image)
Dim wid As Single = bm_in.Width
Dim hgt As Single = bm_in.Height
Dim corners As Point() = { _
New Point(0, 0), _
New Point(wid, 0), _
New Point(0, hgt), _
New Point(wid, hgt)}
Next I grab the center of the image - the point we want to rotate around - and subtract from each of the corners.
Dim cx As Single = wid / 2
Dim cy As Single = hgt / 2
Dim i As Long
For i = 0 To 3
corners(i).X -= cx
corners(i).Y -= cy
Next
Now we need to get the Sine of theta, and the Cosine of theta - which means we need theta.
Dim theta As Single = CSng((_degree) * _direction) * PI / 180
Dim sin_theta As Single = Sin(theta)
Dim cos_theta As Single = Cos(theta)
Here is where the magic comes in, we need to apply the rotation formulas to all the corners.
Dim X As Single
Dim Y As Single
For i = 0 To 3
X = corners(i).X
Y = corners(i).Y
corners(i).X = (X * cos_theta) - (Y * sin_theta)
corners(i).Y = (Y * cos_theta) + (X * sin_theta)
Next
OK, we've got the rotated corners, let's fix the offset we created when finding the rotation point, and for that we'll need the minimum x and y values.
Dim xmin As Single = corners(0).X
Dim ymin As Single = corners(0).Y
For i = 1 To 3
If xmin > corners(i).X Then xmin = corners(i).X
If ymin > corners(i).Y Then ymin = corners(i).Y
Next
For i = 0 To 3
corners(i).X -= xmin
corners(i).Y -= ymin
Next
Now we can actually output the rotated image, but first I create a region using a function to create it based on these corners.
Dim bm_out As New Bitmap(CInt(-2 * xmin), CInt(-2 * ymin))
Dim bgr As Graphics = Graphics.FromImage(bm_out)
Dim rg As Region = CreateTransRegion(corners)
Dim tp As Point = corners(3)
ReDim Preserve corners(2)
bgr.DrawImage(bm_in, corners)
Now we have the rotated image in an output buffer, plus a region to allow transparency for the parts of the control that won't draw the image. Now comes the implementation of the SizeMode
. For StretchImage
, we'll need the width and height of the rotated image - this is easy as it's stored in the corners
array at index 3 - and for stretching or centering we'll need a new region.
Dim gr_out As Graphics = pe.Graphics
gr_out.FillRectangle(New SolidBrush(Me.BackColor), 0, 0, Me.Width, Me.Height)
bm_in.MakeTransparent(_transColor)
If _sizemode = PictureBoxSizeMode.StretchImage Then
Dim maxW As Integer = tp.X
Dim maxH As Integer = tp.Y
For t As Integer = 0 To 2
If maxW < corners(t).X Then maxW = corners(t).X
If maxH < corners(t).Y Then maxH = corners(t).Y
Next
Dim hscale As Double = Me.Width / maxW
Dim vscale As Double = Me.Height / maxH
corners(0) = New Point(corners(0).X * hscale, corners(0).Y * vscale)
corners(1) = New Point(corners(1).X * hscale, corners(1).Y * vscale)
corners(2) = New Point(corners(2).X * hscale, corners(2).Y * vscale)
gr_out.DrawImage(bm_out, 0, 0, Me.Width, Me.Height)
Dim np(3) As Point
np(0) = corners(0)
np(1) = corners(1)
np(2) = corners(2)
np(3) = New Point(tp.X * hscale, tp.Y * vscale)
rg = CreateTransRegion(np)
We don't need quite so much for centering the image.
ElseIf _sizemode = PictureBoxSizeMode.CenterImage Then
Dim wadd As Integer = CInt((Me.Width / 2) - (bm_out.Width / 2))
Dim hadd As Integer = CInt((Me.Height / 2) - (bm_out.Height / 2))
corners(0) = New Point(corners(0).X + wadd, corners(0).Y + hadd)
corners(1) = New Point(corners(1).X + wadd, corners(1).Y + hadd)
corners(2) = New Point(corners(2).X + wadd, corners(2).Y + hadd)
gr_out.DrawImage(bm_in, corners)
Dim np(3) As Point
np(0) = corners(0)
np(1) = corners(1)
np(2) = corners(2)
np(3) = New Point(tp.X + wadd, tp.Y + hadd)
rg = CreateTransRegion(np)
Any other size modes just get output to 0,0, so we don't need anything special there, except for AutoSize
- which just changes the control's size to match the image.
Else
gr_out.DrawImage(bm_in, corners)
End If
If _sizemode = PictureBoxSizeMode.AutoSize Then
MyBase.Width = bm_out.Width
MyBase.Height = bm_out.Height
End If
The last thing we take care of is the region (for transparency), but if the property ShowThrough
is changed to False
then we need to make sure to get rid of any existing region.
Me.Region = Nothing
If _showThrough Then
Me.Region = rg
End If
And that's it! It is certainly possible to modify it to rotate around a point other than the center (although I haven't tried).