This exercise has forced me to learn a lot about the graphics object and graphics transformations.

The way I handle this problem is to create a module-level graphics object which will handle all of our drawing. We can at any time access the contents of the grapics object through it's associated bitmap. We need two module-level bitmaps: one to hold the original picture which we will continually redraw to the graphics object; and the other to store the pixel data of the graphics object.

At the Module level
VB Code:
  1. [FONT=Courier New]    [COLOR=#0000FF]Private[/COLOR] OrigBitMap [COLOR=#0000FF]As[/COLOR] Bitmap
  2.     [COLOR=#0000FF]Private[/COLOR] bm [COLOR=#0000FF]As[/COLOR] Bitmap
  3.     [COLOR=#0000FF]Private[/COLOR] g [COLOR=#0000FF]As[/COLOR] Graphics[/FONT]
In the form
VB Code:
  1. [FONT=Courier New]        [COLOR=#007A00]'OrigBitMap will hold our original picture
  2. [/COLOR]        OrigBitMap = [COLOR=#0000FF]New[/COLOR] Bitmap(lAppPath & "[COLOR=#7A0000]\Tiger.bmp[/COLOR]")
  3.         PictureBox1.Width = CInt(OrigBitMap.Width * 2)
  4.         PictureBox1.Height = CInt(OrigBitMap.Height * 2)
  5.         PosX = CType((PictureBox1.ClientSize.Width - OrigBitMap.Width) / 2, [COLOR=#0000FF]Single[/COLOR])
  6.         PosY = CType((PictureBox1.ClientSize.Height - OrigBitMap.Height) / 2, [COLOR=#0000FF]Single[/COLOR])
  7.  
  8.         [COLOR=#007A00]'associate a bitmap (bm) with the drawing surface (g)
  9. [/COLOR]        [COLOR=#007A00]'make it the same size as our picturebox
  10. [/COLOR]        bm = [COLOR=#0000FF]New[/COLOR] Bitmap(PictureBox1.ClientSize.Width, PictureBox1.ClientSize.Height)
  11.         g = Graphics.FromImage(bm)
  12.         [COLOR=#007A00]'our 1st transformation puts the image in the middle
  13. [/COLOR]        g.TranslateTransform(PosX, PosY, MatrixOrder.Append)
  14.         g.DrawImage(OrigBitMap, 0, 0)
  15.         [COLOR=#007A00]'show the image in the picturebox
  16. [/COLOR]        PictureBox1.Image = bm[/FONT]
Note that all the transformations are cumulative. This means that if we want to rotate by a certain amount, we have to know how much we've rotated already. Same goes for scaling. Here are the new functions for rotation and scaling.
VB Code:
  1. [FONT=Courier New]    [COLOR=#0000FF]Public[/COLOR] [COLOR=#0000FF]Sub[/COLOR] RotateImage([COLOR=#0000FF]ByVal[/COLOR] Gr [COLOR=#0000FF]As[/COLOR] Graphics, [COLOR=#0000FF]ByVal[/COLOR] mRotAngle [COLOR=#0000FF]As[/COLOR] [COLOR=#0000FF]Single[/COLOR])
  2.         [COLOR=#0000FF]Static[/COLOR] OldAngle [COLOR=#0000FF]As[/COLOR] [COLOR=#0000FF]Single[/COLOR] = 0
  3.         [COLOR=#007A00]'move center of image to origin before rotating
  4. [/COLOR]        Gr.TranslateTransform(-CSng(PictureBox1.ClientSize.Width / 2), _
  5.         -CSng(PictureBox1.ClientSize.Height / 2), MatrixOrder.Append)
  6.         [COLOR=#007A00]'rotate image by the additional amount
  7. [/COLOR]        Gr.RotateTransform(mRotAngle - OldAngle, MatrixOrder.Append)
  8.         [COLOR=#007A00]'move image center back to picbox center
  9. [/COLOR]        Gr.TranslateTransform(CSng(PictureBox1.ClientSize.Width / 2), _
  10.         CSng(PictureBox1.ClientSize.Height / 2), MatrixOrder.Append)
  11.         [COLOR=#007A00]'clear old drawing
  12. [/COLOR]        Gr.Clear(PictureBox1.BackColor)
  13.         [COLOR=#007A00]'draw new image
  14. [/COLOR]        Gr.DrawImage(OrigBitMap, 0, 0)
  15.         [COLOR=#007A00]'save current rotation angle
  16. [/COLOR]        OldAngle = mRotAngle
  17.     [COLOR=#0000FF]End[/COLOR] [COLOR=#0000FF]Sub
  18. [/COLOR]
  19.     [COLOR=#0000FF]Public[/COLOR] [COLOR=#0000FF]Sub[/COLOR] ZoomImage([COLOR=#0000FF]ByVal[/COLOR] Gr [COLOR=#0000FF]As[/COLOR] Graphics, [COLOR=#0000FF]ByVal[/COLOR] scalex [COLOR=#0000FF]As[/COLOR] [COLOR=#0000FF]Single[/COLOR] = 1, [COLOR=#0000FF]ByVal[/COLOR] scaley [COLOR=#0000FF]As[/COLOR] [COLOR=#0000FF]Single[/COLOR] = 1)
  20.         [COLOR=#007A00]'move center of image to origin before scaling
  21. [/COLOR]        Gr.TranslateTransform(-CSng(PictureBox1.ClientSize.Width / 2), _
  22.         -CSng(PictureBox1.ClientSize.Height / 2), MatrixOrder.Append)
  23.         [COLOR=#007A00]'scale image by new factor
  24. [/COLOR]        Gr.ScaleTransform(scalex, scaley, MatrixOrder.Append)
  25.         [COLOR=#007A00]'move image center back to picbox center
  26. [/COLOR]        Gr.TranslateTransform(CSng(PictureBox1.ClientSize.Width / 2), _
  27.         CSng(PictureBox1.ClientSize.Height / 2), MatrixOrder.Append)
  28.         [COLOR=#007A00]'clear old drawing
  29. [/COLOR]        Gr.Clear(PictureBox1.BackColor)
  30.         [COLOR=#007A00]'draw new image
  31. [/COLOR]        Gr.DrawImage(OrigBitMap, 0, 0)
  32.     [COLOR=#0000FF]End[/COLOR] [COLOR=#0000FF]Sub[/COLOR][/FONT]
And these are called from the trackbars like so
VB Code:
  1. [FONT=Courier New]    [COLOR=#0000FF]Private[/COLOR] [COLOR=#0000FF]Sub[/COLOR] TrackRotation_Scroll([COLOR=#0000FF]ByVal[/COLOR] sender [COLOR=#0000FF]As[/COLOR] System.Object, _
  2.     [COLOR=#0000FF]ByVal[/COLOR] e [COLOR=#0000FF]As[/COLOR] System.EventArgs) [COLOR=#0000FF]Handles[/COLOR] TrackRotation.Scroll
  3.         [COLOR=#007A00]'perform a rotation transformation on the graphics object
  4. [/COLOR]        RotateImage(g, TrackRotation.Value)
  5.         [COLOR=#007A00]'show the results
  6. [/COLOR]        PictureBox1.Image = bm
  7.     [COLOR=#0000FF]End[/COLOR] [COLOR=#0000FF]Sub
  8. [/COLOR]
  9.     [COLOR=#0000FF]Private[/COLOR] [COLOR=#0000FF]Sub[/COLOR] TrackZoom_Scroll([COLOR=#0000FF]ByVal[/COLOR] sender [COLOR=#0000FF]As[/COLOR] System.Object, _
  10.     [COLOR=#0000FF]ByVal[/COLOR] e [COLOR=#0000FF]As[/COLOR] System.EventArgs) [COLOR=#0000FF]Handles[/COLOR] TrackZoom.Scroll
  11.         [COLOR=#0000FF]Static[/COLOR] oldScaleX [COLOR=#0000FF]As[/COLOR] [COLOR=#0000FF]Single[/COLOR] = 1
  12.         [COLOR=#0000FF]Static[/COLOR] oldScaleY [COLOR=#0000FF]As[/COLOR] [COLOR=#0000FF]Single[/COLOR] = 1
  13.         [COLOR=#0000FF]Dim[/COLOR] ScaleX [COLOR=#0000FF]As[/COLOR] [COLOR=#0000FF]Single
  14. [/COLOR]        [COLOR=#0000FF]Dim[/COLOR] ScaleY [COLOR=#0000FF]As[/COLOR] [COLOR=#0000FF]Single
  15. [/COLOR]        [COLOR=#007A00]'Since all the transformations are cumulative,
  16. [/COLOR]        [COLOR=#007A00]'we have to keep track of the previous scaling factors
  17. [/COLOR]        [COLOR=#007A00]'differences in scaling are ratios
  18. [/COLOR]        [COLOR=#0000FF]If[/COLOR] ChkZoomX.Checked [COLOR=#0000FF]Then
  19. [/COLOR]            ScaleX = CType(TrackZoom.Value / 100 / oldScaleX, [COLOR=#0000FF]Single[/COLOR])
  20.             oldScaleX = CSng(TrackZoom.Value / 100)
  21.         [COLOR=#0000FF]End[/COLOR] [COLOR=#0000FF]If
  22. [/COLOR]        [COLOR=#0000FF]If[/COLOR] ChkZoomY.Checked [COLOR=#0000FF]Then
  23. [/COLOR]            ScaleY = CType(TrackZoom.Value / 100 / oldScaleY, [COLOR=#0000FF]Single[/COLOR])
  24.             oldScaleY = CSng(TrackZoom.Value / 100)
  25.         [COLOR=#0000FF]End[/COLOR] [COLOR=#0000FF]If
  26. [/COLOR]        [COLOR=#007A00]'scale the graphics object and display results
  27. [/COLOR]        ZoomImage(g, Scalex, Scaley)
  28.         PictureBox1.Image = bm
  29.     [COLOR=#0000FF]End[/COLOR] [COLOR=#0000FF]Sub[/COLOR][/FONT]
Notice that in the TrackZoom Scroll we have to form the ratio of the previous zoom position to the new zoom position to get the new zoom factor. In the case rotation it is easier since rotation is additive.

Finally, if you want to access the current image simply access the associated bitmap.
VB Code:
  1. [FONT=Courier New]    [COLOR=#0000FF]Private[/COLOR] [COLOR=#0000FF]Sub[/COLOR] Button2_Click([COLOR=#0000FF]ByVal[/COLOR] sender [COLOR=#0000FF]As[/COLOR] System.Object, [COLOR=#0000FF]ByVal[/COLOR] e [COLOR=#0000FF]As[/COLOR] System.EventArgs) [COLOR=#0000FF]_
  2.        Handles[/COLOR] Button2.Click
  3.         [COLOR=#007A00]'How to save transformed image to file here?
  4. [/COLOR]        PictureBox2.Image = bm
  5.     [COLOR=#0000FF]End[/COLOR] [COLOR=#0000FF]Sub[/COLOR][/FONT]