I'm working on making a paint-esq image manipulator in VB.Net, and I'm still new to vb. I want the user to be able to upload an image and make adjustments to it, such as adding lines and text. I also want the user to be able to transfer the drawings and text they added to a different baseimage. For example, if the user draws a dog on top of a picture of a park, they can change it so the dog is on a street instead.
I've been messing with the idea of loading the image as the picturebox.backgroundImage, but running into difficulties changing the backgroundImage without reseting the drawings and with croping the image. I've also been dabling in having two pictureboxes with the one on top for drawings, but I'm running into transparency and cropping issues
Here is the code I'm using to establish my picturebox by setting the base image as .backgroundImage
Private Sub LoadImage(thisImage As Image)
'we set the picturebox size according to image, we can get image width and height with the help of Image.Width and Image.height properties.
img.BackgroundImage = thisImage 'c'
img.Image = New Bitmap(thisImage.Width, thisImage.Height) 'c'
img.BorderStyle = BorderStyle.FixedSingle
End Sub
example of the image maniputlation
Private Sub ButtonDone_Click(sender As Object, e As EventArgs) Handles ButtonDone.Click, DoneToolStripMenuItem.Click
Cursor = Cursors.Default
Select Case LCase(stateFlag)
Case "header"
'Reset stuff back to normal
ButtonHeader.Text = "Header"
stateFlag = ""
Cancel_Button.Enabled = False
'set up space to draw on the image
Dim newBm As New Bitmap(img.Image.Width, img.Image.Height)
' First we define a rectangle with the help of already calculated points
Dim newGraphics As Graphics = Graphics.FromImage(newBM) ' create graphics
newGraphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
newGraphics.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
newGraphics.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
'set image attributes
newGraphics.DrawImage(img.Image, New Rectangle(0, 0, img.Image.Width + 1, img.Image.Height + 1), _
0, 0, img.Image.Width + 1, img.Image.Height + 1, GraphicsUnit.Pixel)
'Draw Edges for header
newGraphics.DrawLine(Pens.Black, startPoint.X, borderSize - 20, startPoint.X, borderSize - 50)
newGraphics.DrawLine(Pens.Black, endPoint.X, borderSize - 20, endPoint.X, borderSize - 50)
Dim drawFont As New Font("Times New Roman", 12)
Dim drawBrush As New SolidBrush(Color.Black)
Dim stringSize As SizeF = newGraphics.MeasureString(HeaderLabel.Text, drawFont)
' Draw header label inbetween the two edges.
newGraphics.DrawString(HeaderLabel.Text, drawFont, drawBrush, (startPoint.X + endPoint.X) / 2 - (stringSize.Width / 2), borderSize - 45)
img.Image = newBm
PushUndo(img.Image.Clone)
End Sub
I would advise trying the following method to use one picturebox on top of the other, it is a lot simpler than some other methods. In your form load handler, do something like:
pctBackground.BackgroundImage = Bitmap.FromFile("park.jpg")
pctForeground.BackColor = Color.Transparent
pctForeground.Parent = pctBackground
pctForeground.Image = New Bitmap(pctForeground.ClientSize.Width, pctForeground.ClientSize.Height)
Then when you have drawn on the pctForeground, save it like:
pctForeground.Image.Save("dog_in_park.png", System.Drawing.Imaging.ImageFormat.Png)
Related
On form load event I create graphics from an empty (no image) picturebox called PB_Pixel_Layout.
Private Sub Main_From_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Pixel_Graphics = PB_Pixel_Layout.CreateGraphics
End Sub
I then draw a bunch of filled ellipses through the SetPixel(). The ellipses get drawn on the picturebox but takes 2 cycles of the the SetPixel() to show. There is also a double window image when the program starts the causes a lag and not sure what is causing the problem but I assume I have not setup the graphics correctly. I tried to run by creating a bitmap at run time instead of using a picturebox and it worked fine so the issue is narrowed down to the create graphics from picturebox. I also tried to loading an image to the picturebox properties but did not make a difference.
Private Sub SetPixel_Grid_Bitmap()
Pen_Grid = New Pen(Color.Gray)
Selected_Pen = New Pen(Color.Yellow)
'draw
For Col = 0 To intColumnCount
For Row = 0 To intRowCount
B_Color = Color.FromArgb(intPatternColorsRed(strhexPixelHexValue(intCounter + intBank)), intPatternColorsGreen(strhexPixelHexValue(intCounter + intBank)), intPatternColorsBlue(strhexPixelHexValue(intCounter + intBank)))
Brush_B = New SolidBrush(B_Color)
'// Grid
Pixel_Graphics.DrawEllipse(Pen_Grid, StartLocation.X + (Col * (intScale + 6)), StartLocation.Y + (Row * (intScale + 6)), intScale, intScale)
'// Fill with color
Pixel_Graphics.FillEllipse(Brush_B, StartLocation.X + (Col * (intScale + 6)) + 2, StartLocation.Y + (Row * (intScale + 6)) + 2, intScale - 4, intScale - 4)
'// Selected
If ArrPixelData_Array(intCounter)(P_Selected) = 1 Then
Pixel_Graphics.DrawEllipse(Selected_Pen, StartLocation.X + (Col * (intScale + 6)), StartLocation.Y + (Row * (intScale + 6)), intScale, intScale)
End If
intCounter = intCounter + 1
Next Row
Next Col
End Sub
Here is the update
Public Sub RefreshDrawing()
bm = New Bitmap(PB_Pixel_Layout.Width, PB_Pixel_Layout.Height)
Using g As Graphics = Graphics.FromImage(BB)
g.SmoothingMode = SmoothingMode.AntiAlias
g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias
End Using
PB_Pixel_Layout.Image = bm ' assign drawn bitmap to picturebox
End Sub
I would suggest to declare a global bitmap
Dim bm As Bitmap
And create a refresh function like (just a general suggestion):
Public Sub RefreshDrawing()
bm = New Bitmap(Me.Drawing.Width, Me.Drawing.Height)
Using g = Graphics.FromImage(bm)
g.SmoothingMode = SmoothingMode.AntiAlias
g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias
Dim r as New RectangleF (200, 100, 400, 300)
g.FillEllipse(pDrwStyle.dstBrush, r) ' draw inside points (area)
g.DrawEllipse(pDrwStyle.dstPen, Rectangle.Round(r)) ' draw ellipse border
End Using
Me.PictureBox1.Image = bm ' assign drawn bitmap to picturebox
End Sub
And in you load event (and perhaps a Refresh button) put simply:
Call RefreshDrawing()
This is very fast and reliable way of drawing graphics.
I would suggest not to use Paint event. Only for temporary drawing, such as drawing in MouseMove mode and such.
As an Alternate suggestion, you could draw it all to a buffer then render that when required.
Like this:
Dim myContext As New BufferedGraphicsContext
Dim mybuff As System.Drawing.BufferedGraphics
(As Globals)
Then add this* before you start drawing anything.
mybuff = myContext.Allocate(picturebox1.CreateGraphics, rectangle)
mybuff.Graphics.CompositingMode = Drawing2D.CompositingMode.SourceOver
mybuff.Graphics.CompositingQuality = Drawing2D.CompositingQuality.AssumeLinear
mybuff.Graphics.SmoothingMode = Drawing2D.SmoothingMode.None
mybuff.Graphics.InterpolationMode = CType(Drawing2D.QualityMode.Low, Drawing2D.InterpolationMode)
mybuff.Graphics.PixelOffsetMode = Drawing2D.PixelOffsetMode.None
*Only first line is required the rest is an example custom configuration.
rectangle is the size of your buffer, it needs to be at least as large as the images destination (picturebox1)
To draw into the buffer just type mybuff.Graphics...
eg.
mybuff.Graphics.FillRectangle(colbrush,50,50, 4, 4)
mybuff.Graphics.FillEllipse(colbrush, 100, 100, 50, 30)
...
When done building the image it needs to be rendered/painted onto the target (picturebox1) like so:
mybuff.Render
The render does not need to come directly after the drawing it can be called from anywhere in your code. So its up to you when it is painted and how often.
Also doing it like this is much faster than drawing directly to a target.
Lastly dispose of my buff and mycontext when you are done with them.
[EDIT]
Forgot to mention that you can also render the buffer to other targets like so:
Mybuff.Render(picturebox2.CreateGraphics)
Mybuff.Render(picturebox47.CreateGraphics)
I have a software that builds a 3D text by using grafx.DrawString() and I need to copy this graphic to clipboard. When I try to do so, it throws a NullReferenceException.
How can I copy the graphics drawn on a PictureBox?
This is the code to draw the text:
Dim grafx As Graphics
Private Sub draw_block_text10()
Dim text_size As SizeF
Dim back_brush As Brush = Brushes.Black 'COLOR FOR THE BOARDER TEXT
Dim fore_brush As Brush = Brushes.Blue 'COLOR FOR THE MAIN TEXT
Dim fnt As New Font("Microsoft Sans Serif", NumericUpDown1.Value, FontStyle.Regular)
Dim location_x, location_y As Single 'USED IT FOR THE LOCATION
Dim i As Integer
'CREATE A GRAPHIC OBJECT IN THE PICTUREBOX.
grafx = Me.PictureBox2.CreateGraphics()
'CLEAR THE PICTUREBOX
grafx.Clear(Color.White)
'LOOK THE REQUIRED SIZE TO DRAW THE TEXT
text_size = grafx.MeasureString(Me.TextBox1.Text, fnt)
'ELIMINATE THE REDUNDANT CAlCULATION AFTER GETTING THE LOCATION.
location_x = (Me.PictureBox2.Width - text_size.Width) / 2
location_y = (Me.PictureBox2.Height - text_size.Height) / 2
'FIRST, DRAW THE BLACK BACKGROUND TO GET THE EFFECT,
'AND THE TEXT MUST BE DRAWN REAPETEDLY FROM THE OFFSET RIGHT, UP TO THE MAIN TEXT IS DRAWN.
For i = CInt(nupDepth.Value) To 0 Step -1
grafx.DrawString(TextBox1.Text, fnt, back_brush, _
location_x - i, location_y + i)
Next
Dim mydataandtimeforsave = DateTime.Now.ToString("yyyyMMddHHmmss")
'DRAW THE ROYAL BLUE FOR THE MAIN TEXT OVER THE BLACk TEXT
grafx.DrawString(TextBox1.Text, fnt, fore_brush, location_x, location_y)
Dim bmp As New Bitmap(Me.PictureBox2.Width, Me.PictureBox2.Height)
Dim g As Graphics = Graphics.FromImage(bmp)
g.Clear(Color.Transparent)
''Perform Drawing here
End Sub
This is the code to copy to clipboard:
Clipboard.SetDataObject( _
DirectCast(PictureBox2.Image.Clone, Bitmap), _
True)
Beep()
Using a Graphics object created from a PictureBox control (PictureBox.CreateGraphics()) to draw on doesn't actually set/change the Image property of the PictureBox. You can confirm that by checking for PictureBox2.Image Is Nothing, which will return true if the PictureBox had no image before drawing on it.
Instead, create an Image with the dimensions of the PictureBox, use Graphics.FromImage() to create your Graphics object, draw what you need to draw, and then assign the image to the PictureBox.Image property.
Something like this should work fine:
Dim bmp As New Bitmap(PictureBox2.Width, PictureBox2.Height)
Using g As Graphics = Graphics.FromImage(bmp)
g.Clear(Color.White)
text_size = g.MeasureString(Me.TextBox1.Text, fnt)
location_x = (Me.PictureBox2.Width - text_size.Width) / 2
location_y = (Me.PictureBox2.Height - text_size.Height) / 2
For i = CInt(nupDepth.Value) To 0 Step -1
g.DrawString(TextBox1.Text, fnt, back_brush, location_x - i, location_y + i)
Next
g.DrawString(TextBox1.Text, fnt, fore_brush, location_x, location_y)
End Using
PictureBox2.Image = bmp
Note: Always remember to dispose the created Graphics object when you finish using it either by calling .Dispose() or by wrapping it in a Using statement like what I did above.
Instead of
Clipboard.SetDataObject(DirectCast(PictureBox2.Image.Clone, Bitmap), True)
Use
Clipboard.SetDataObject(PictureBox2.Image, 2)
I found an example online of how to create a business card listview item using the ObjectListView. It demonstrates using a custom renderer to manually draw each Tile within the control.
The sample involves creating buffered graphics and manually drawing each item. I have this working, however, I am finding that all text I draw on the tile is looking pixelated (no matter what settings I use), especially if I draw small text. For example, using a default system font on a normal form looks fine, but in my renderer it looks jagered.
The code looks like this:
Imports BrightIdeasSoftware
Imports System.Drawing.Drawing2D
Imports System.IO
Public Class StoreListRenderer
Inherits AbstractRenderer
Public Overrides Function RenderItem(e As DrawListViewItemEventArgs, g As Graphics, itemBounds As Rectangle, rowObject As Object) As Boolean
' If we're in any other view than Tile, return false to say that we haven't done
' the rendereing and the default process should do it's stuff
Dim olv As ObjectListView = TryCast(e.Item.ListView, ObjectListView)
If olv Is Nothing OrElse olv.View <> View.Tile Then
Return False
End If
' Use buffered graphics to kill flickers
Dim buffered As BufferedGraphics = BufferedGraphicsManager.Current.Allocate(g, itemBounds)
g = buffered.Graphics
g.Clear(olv.BackColor)
g.SmoothingMode = ObjectListView.SmoothingMode
g.TextRenderingHint = ObjectListView.TextRenderingHint
If e.Item.Selected Then
Me.BorderPen = Pens.White
Me.HeaderBackBrush = New SolidBrush(olv.HighlightBackgroundColorOrDefault)
Else
Me.BorderPen = New Pen(Color.FromArgb(&H33, &H33, &H33))
Me.HeaderBackBrush = New SolidBrush(Color.FromArgb(&H33, &H33, &H33))
End If
DrawStoreCard(g, itemBounds, rowObject, olv, DirectCast(e.Item, OLVListItem))
' Finally render the buffered graphics
buffered.Render()
buffered.Dispose()
' Return true to say that we've handled the drawing
Return True
End Function
Friend BorderPen As New Pen(Color.FromArgb(&H33, &H33, &H33))
Friend TextBrush As Brush = New SolidBrush(Color.FromArgb(&H22, &H22, &H22))
Friend HeaderTextBrush As Brush = Brushes.AliceBlue
Friend HeaderBackBrush As Brush = New SolidBrush(Color.FromArgb(&H33, &H33, &H33))
Friend BackBrush As Brush = Brushes.LemonChiffon
Friend BackgroundBrush As Brush = New SolidBrush(Color.FromArgb(38, 38, 38))
Public Sub DrawStoreCard(g As Graphics, itemBounds As Rectangle, rowObject As Object, olv As ObjectListView, item As OLVListItem)
Try
Dim _store As StoreObject = TryCast(rowObject, StoreObject)
' Allow a border around the card
itemBounds.Inflate(-5, -5)
g.FillRectangle(BackgroundBrush, itemBounds)
Dim ColouredPanelRect As Rectangle = New Rectangle(itemBounds.Left + 7, itemBounds.Top + 7, 70, 70)
g.FillRectangle(Brushes.IndianRed, ColouredPanelRect)
Dim fmt As New StringFormat()
fmt.Alignment = StringAlignment.Center
fmt.LineAlignment = StringAlignment.Center
For i As Integer = 0 To olv.Columns.Count - 1
Dim column As OLVColumn = olv.GetColumn(i)
If column.IsTileViewColumn Then
'Draw Store Number
Using font As New Font(fontMgr("Mentone Lig"), 36, FontStyle.Regular, GraphicsUnit.Pixel)
g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
g.DrawString(_store.StoreID, font, BackgroundBrush, ColouredPanelRect, fmt)
End Using
'Draw Store Name
Using string_format As New StringFormat()
string_format.Alignment = StringAlignment.Center
string_format.LineAlignment = StringAlignment.Near
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit
g.DrawString(_store.StoreName.ToUpper, New Font("Segoe UI", 9, FontStyle.Regular), Brushes.White, itemBounds, string_format)
End Using
...
I have tried playing around AntiAlias and ClearType but both have the same affect.
UPDATE
Here are my results below with using the same font at the same size:
The text at the top represents a normal label placed on a form. The text at the bottom is text drawn on to a listview item (in Tile mode) using the code above. You can easily identify the difference and see that it is not as smooth. Also the larger I make the text the more unsmooth it gets. I have tried playing around with all the settings and currently have it set to:
g.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
g.PixelOffsetMode = PixelOffsetMode.HighQuality
Thanks
Okay, I ended up working this out by going through the example again.
The problem was that I was dropping the scaling element from my code which seemed to be causing the pixilation of the text.
By adding the following code back in, it has corrected the smoothness of the text:
Dim size As SizeF = g.MeasureString(txt, font, CInt(itemBounds.Width), fmt)
g.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
So my overall code looks like this:
Using font As New Font("Segoe UI", 9, FontStyle.Regular)
' Measure the height of the title
txt = column.GetStringValue(rowObject)
Dim size As SizeF = g.MeasureString(txt, font, CInt(itemBounds.Width), fmt)
g.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
g.PixelOffsetMode = PixelOffsetMode.HighQuality
g.DrawString(txt, font, Brushes.LightGray, itemBounds, fmt)
End Using
I currently have a picture box where the user will click and drag to draw a rectangle over an image (one that can be changed regularly). When they're done (mouse_up), I will display the relative points of the rect in a text box to the resolution.
So, for example, the user draws from top left (0,0) to bottom right of a 1920 x 680 image (picturebox.right, picturebox.bottom) for a rect, the text box will show (1920,680) for the end point. That's mostly just ratio stuff.
I am using the code from an answer of a previous question of mine (Having trouble drawing simple rectangle in picturebox) to draw it.
The Problem: The box doesn't follow the mouse since the images have to be done in stretch mode. They're usually pretty large (like 1920 x 680) and can't fit in a regular gui. There are multiple resolutions, so got to go dynamic with the ratios. Without editing, this code works great in normal mode, but that doesn't work for usability. So, when you draw the box, it's really small and not relative to the mouse (so I can't display the end point on the textboxes).
Here's an example of what I mean. I've dragged my mouse halfway across the image:
What I've tried: I've attempted to counter act it by ratios, but it still doesn't fix the displaying the end point issue, or does it really follow the mouse that well. It's usually off by at least 10 or so pixels to the left. Here's my adjusted code for that:
Private Sub DrawRectangle(ByVal pnt As Point)
Try
Dim g As Graphics
g = Graphics.FromImage(img)
g.DrawImage(imgClone, 0, 0) 'we are clearing img with imgClone. imgClone contains the original image without the rectangles
Dim w_ratio As Integer = Math.Floor(img.Width / pbZoneImage.Width)
Dim h_ratio As Integer = Math.Floor(img.Height / pbZoneImage.Height)
Dim customPen As New Pen(currentColor, 5)
'If pnt.X = mouse_Down.X Or pnt.Y = mouse_Down.Y Then
' g.DrawLine(customPen, mouse_Down.X, mouse_Down.Y, pnt.X * w_ratio, pnt.Y * h_ratio)
'Else
theRectangle = New Rectangle(Math.Min(mouse_Down.X, pnt.X * w_ratio), Math.Min(mouse_Down.Y, pnt.Y * h_ratio),
Math.Abs(mouse_Down.X - pnt.X * w_ratio), Math.Abs(mouse_Down.Y - pnt.Y * h_ratio))
g.DrawRectangle(customPen, theRectangle)
'End If
g.Dispose()
pbZoneImage.Invalidate() 'draw img to picturebox
Catch ex As Exception
End Try
End Sub
I've also tried just getting the end display point (x,y) to match the relative end of the rectangle, but again it isn't working with the ratios.
Any ideas on how to make this work as well as it does in normal mode as it does in stretch? I'm also open to different controls or just any tips in general. Thanks!
This can be done with many ways but the easiest is to use a picturebox with SizeMode = Normal. Load your images:
img = New Bitmap(pbZoneImage.Width, pbZoneImage.Height)
imgClone = My.Resources.... 'real dimensions
Dim g As Graphics = Graphics.FromImage(img)
'it will scale the image, no need for stretch mode
g.DrawImage(imgClone, 0, 0, pbZoneImage.Width, pbZoneImage.Height)
g.Dispose()
pbZoneImage.Image = img
Then draw normally:
Private Sub DrawRectangle(ByVal pnt As Point)
Try
Dim g As Graphics
g = Graphics.FromImage(img)
g.DrawImage(imgClone, 0, 0, pbZoneImage.Width, pbZoneImage.Height) 'we are clearing img with imgClone. imgClone contains the original image without the rectangles
Dim customPen As New Pen(currentColor, 5)
'If pnt.X = mouse_Down.X Or pnt.Y = mouse_Down.Y Then
' g.DrawLine(customPen, mouse_Down.X, mouse_Down.Y, pnt.X * w_ratio, pnt.Y * h_ratio)
'Else
theRectangle = New Rectangle(Math.Min(mouse_Down.X, pnt.X), Math.Min(mouse_Down.Y, pnt.Y),
Math.Abs(mouse_Down.X - pnt.X), Math.Abs(mouse_Down.Y - pnt.Y))
g.DrawRectangle(customPen, theRectangle)
'End If
g.Dispose()
pbZoneImage.Invalidate() 'draw img to picturebox
Catch ex As Exception
End Try
End Sub
In mouse up event scale to get the correct result:
Private Sub pbZoneImage_MouseUp(sender As System.Object, e As System.Windows.Forms.MouseEventArgs) Handles pbZoneImage.MouseUp
Dim width, height As Integer
width = CInt(Math.Abs(mouse_Down.X - e.X) * (imgClone.Width / pbZoneImage.Width))
height = CInt(Math.Abs(mouse_Down.Y - e.Y) * (imgClone.Height / pbZoneImage.Height))
TextBox1.Text = width.ToString + " " + height.ToString
End Sub
I have problem with Graphics.RotateTransfrom() with the following code :
Dim newimage As Bitmap
newimage = System.Drawing.Image.FromFile("C:\z.jpg")
Dim gr As Graphics = Graphics.FromImage(newimage)
Dim myFontLabels As New Font("Arial", 10)
Dim myBrushLabels As New SolidBrush(Color.Black)
Dim a As String
'# last 2 number are X and Y coords.
gr.DrawString(MaskedTextBox2.Text * 1000 + 250, myFontLabels, myBrushLabels, 1146, 240)
gr.DrawString(MaskedTextBox2.Text * 1000, myFontLabels, myBrushLabels, 1146, 290)
a = Replace(Label26.Text, "[ mm ]", "")
gr.DrawString(a, myFontLabels, myBrushLabels, 620, 1509)
a = Replace(Label5.Text, "[ mm ]", "")
gr.DrawString(a, myFontLabels, myBrushLabels, 624, 548)
gr.RotateTransform(90.0F)
gr.DrawString(a, myFontLabels, myBrushLabels, 0, 0)
PictureBox1.Image = newimage
I dont know why but my image in pictureBox1 is not rotated. Someone known solution ?
The issue at hand is that the RotateTransform method does not apply to the existing image.
Instead, it applies to the transformation matrix of the graphics object. Basically, the transformation matrix modifies the coordinate system used to add new items.
Try the following :
Dim gfx = Graphics.FromImage(PictureBox1.Image)
gfx.DrawString("Test", Me.Font, Brushes.Red, New PointF(10, 10))
gfx.RotateTransform(45)
gfx.DrawString("Rotate", Me.Font, Brushes.Red, New PointF(10, 10))
The first string is drawn normally, while the second is drawn rotated.
So what you need to do is create a new graphics object, apply your rotation, draw your source image onto the graphics (graphics.DrawImage), and then draw all your text :
' Easy way to create a graphisc object
Dim gfx = Graphics.FromImage(PictureBox1.Image)
gfx.Clear(Color.Black)
gfx.RotateTransform(90) ' Rotate by 90°
gfx.DrawImage(Image.FromFile("whatever.jpg"), New PointF(0, 0))
gfx.DrawString("Test", Me.Font, Brushes.Red, New PointF(10, 10))
gfx.DrawString("Rotate", Me.Font, Brushes.Red, New PointF(10, 10))
But beware of rotation, you'll find that you need to change the coordinates at which you draw your image (Or change the RenderingOrigin property of the graphics, setting it to the center of the image makes it easier to handle rotations), otherwise your picture won't be visible (it will be drawn, but off the visible part of the graphics).
Hope that helps