Arint System.Drawing.Graphics suppose to write/modify the image its attached to? - vb.net

As you use a graphics object shouldn't the changes occur on the bitmap (source image) at some point? Running the code below I get 5 images that are all identical to the source. 1.bmp, 2.bmp, 3.bmp, 4.bmp, and 5.bmp are identical to "scaleCharacter" except 4 & 5 have higher compression (smaller file size)
Private Function DrawCharacterMenu() As Boolean
Try
'Background
Dim rect As Rectangle = New Rectangle(100, 100, 128, 128)
Graphics.FromImage(Render).FillRectangle(Brushes.Black, rect)
'Scale up sprite
Dim scaleCharacter As Bitmap = ActiveCharacter.img.Clone
Using grDest = Graphics.FromImage(scaleCharacter)
scaleCharacter.Save("1.bmp")
grDest.ScaleTransform(4.0F, 4.0F)
scaleCharacter.Save("2.bmp")
grDest.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
scaleCharacter.Save("3.bmp")
grDest.DrawImage(scaleCharacter, 0, 0)
scaleCharacter.Save("4.bmp")
End Using
scaleCharacter.Save("5.bmp")
'Draw scaled up sprite to rendering
Graphics.FromImage(Render).DrawImage(scaleCharacter, 100, 100)
Catch ex As Exception
addDebugMessage("Error: Mainmenu.DrawCharacterMenu: " & ex.Message)
Return False
End Try
Return True
End Function
I would Expect 1 to be the same as 'scaleCharacter'
2 and beyond to be 4 times larger (32x32 to 128x128)
3 and beyond to have less interpolation (not looked stretched)
The finished 'scaleCharacter' drawn onto the rendering also is identical to the original image...

All your images are the same because technically you never change them.
Graphics.ScaleTransform() changes only the internal "world" matrix used when drawing primitives. ScaleTransform(4.0F, 4.0F) makes the drawing grid 4x wider and 4x taller, but it doesn't change the image itself until you draw something on it. For instance, if you were to draw a 20 x 10 rectangle on your image now it would result in a rectangle 80 x 40 in size.
To resize the actual image you have to create a new bitmap with the scaled size, then draw the old image scaled onto it.
Changing Graphics.InterpolationMode affects only newly drawn objects. Again it doesn't change your image until you draw something on it.
Finally, while grDest.DrawImage(scaleCharacter, 0, 0) does change your image, it draws the same image in the top-left corner (0, 0) of itself, so there is no visible change.
Here's how you can make it work:
Scaling your image:
'Scale factor.
Dim scaleFactor As Single = 4.0F
'Create a new bitmap of the scaled size.
Using scaledBmp As New Bitmap(scaleCharacter.Width * scaleFactor, scaleCharacter.Height * scaleFactor)
Using g As Graphics = Graphics.FromImage(scaledBmp)
'Draw the old image, scaled, onto the new one.
'srcRect: The rectangle specifying which portion of the source image (scaleCharacter) to draw.
' We want the full image so we specify (0, 0, source width, source height).
'destRect: The rectangle specifying where on the destination image (scaledBmp) to draw the source image.
' Since we want to scale it we specify the full destination image (0, 0, dest width, dest height).
Dim srcRect As New Rectangle(0, 0, scaleCharacter.Width, scaleCharacter.Height)
Dim destRect As New Rectangle(0, 0, scaledBmp.Width, scaledBmp.Height)
g.DrawImage(scaleCharacter, destRect, srcRect, GraphicsUnit.Pixel)
'Save the image.
scaledBmp.Save("2.bmp")
End Using
End Using
Scaling your image using Nearest Neighbour interpolation:
'Create a new bitmap of the scaled size.
Using scaledBmp As New Bitmap(scaleCharacter.Width * scaleFactor, scaleCharacter.Height * scaleFactor)
Using g As Graphics = Graphics.FromImage(scaledBmp)
'Set the interpolation mode before drawing.
g.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
'Draw the old image, scaled, onto the new one.
'srcRect: The rectangle specifying which portion of the source image (scaleCharacter) to draw.
' We want the full image so we specify (0, 0, source width, source height).
'destRect: The rectangle specifying where on the destination image (scaledBmp) to draw the source image.
' Since we want to scale it we specify the full destination image (0, 0, dest width, dest height).
Dim srcRect As New Rectangle(0, 0, scaleCharacter.Width, scaleCharacter.Height)
Dim destRect As New Rectangle(0, 0, scaledBmp.Width, scaledBmp.Height)
g.DrawImage(scaleCharacter, destRect, srcRect, GraphicsUnit.Pixel)
'Save the image.
scaledBmp.Save("3.bmp")
End Using
End Using

Related

How to Modify a iTextSharp.text.Rectangle Top left position in VB?

I am Using itextsharp dll in VB to generate a PDF. In the PDF, need to increase space between rectangle and text. I tried to setmargin and changed the rectangle coordinates, but the space between top rectangle and text is not creating. Please see the attachment.
Below is the code i am using to generate PDF
Dim reader As PdfReader = New PdfReader(tempFile)
Dim size As Rectangle = reader.GetPageSize(1)
Dim AcroAVDoc As Document = New Document(iTextSharp.text.PageSize.A4.Rotate())
AcroAVDoc.SetMargins(0, 0, 0, 0)
Dim FS As FileStream = New FileStream(newFile, FileMode.Create, FileAccess.Write)
Dim writer As PdfWriter = PdfWriter.GetInstance(AcroAVDoc, FS)
AcroAVDoc.Open()
If (SaveDoc) Then
Dim cb As PdfContentByte = writer.DirectContent
Dim bf As BaseFont = BaseFont.CreateFont(BaseFont.TIMES_ROMAN, BaseFont.CP1252, BaseFont.NOT_EMBEDDED)
cb.SetColorFill(BaseColor.BLACK)
cb.SetFontAndSize(bf, 11)
cb.BeginText()
Dim text As String = "HeaderText " + TableData(HeaderText)
''put the alignment And coordinates here
cb.SetTextMatrix(240, 583)
cb.ShowText(text)
cb.EndText()
''create the New page And add it to the pdf
Dim Page As PdfImportedPage = writer.GetImportedPage(reader, 1)
Dim psize As Rectangle = reader.GetPageSizeWithRotation(1)
cb.AddTemplate(Page, 0, -1.0F, 1.0F, 0, 0, psize.Height)
'AcroAVDoc.Save(1, newFile)
End If
AcroAVDoc.Close()
FS.Close()
writer.Close()
reader.Close()
What your code does (if SaveDoc is true) is
creating a new PDF file in newFile with landscape A4 page size,
drawing the string text on its first page, and
drawing the rotated static contents of the first page of the PDF in tempFile on the same first page.
Thus, to modify the position of the text relative to content copied from the other file, you have to adjust the position where you draw the text and/or the position where you draw the imported content.
You set the position of the text you draw by setting the text matrix (translation coordinates only):
cb.SetTextMatrix(240, 583)
You set the position of the imported page content you draw by setting the transformation matrix (full matrix):
cb.AddTemplate(Page, 0, -1.0F, 1.0F, 0, 0, psize.Height)
The second through fifth parameter (0, -1.0F, 1.0F, 0) define a clockwise rotation by 90° around the bottom left corner and the sixth and seventh parameter (0, psize.Height) define an upwards translation. As the rotation around the bottom left corner rotates the content out of the visible page area, that upwards translation is needed to move it back into view.
To tweak the position of the text in relation to the imported page, simply experiment with changing the 240, 583 for the text and/or the 0, psize.Height for the imported page a bit (e.g. plus or minus 10) until it matches your expectation.

Split an Image into different PictureBoxes

I have a Image with size 187x16 which contain 10 smaller Images in a row.
I want split those Images into 10 different PictureBoxes.
Original Image:
Dim fr_bm As New Bitmap(Image.FromFile(AppDomain.CurrentDomain.BaseDirectory & "/images/u/image.gif"))
Dim to_bm As New Bitmap(16, 16)
Dim unitsimagearray(9) As Image
Dim gr As Graphics = Graphics.FromImage(to_bm)
For i As Integer = 0 To 9
Dim fr_rect As New Rectangle(i * 19, 0, 16, 16) '0,19,38,76
Dim to_rect As New Rectangle(0, 0, 16, 16)
gr.DrawImage(fr_bm, to_rect, fr_rect, GraphicsUnit.Pixel)
unitsimagearray(i) = to_bm
Next
u1.Image = unitsimagearray(0)
But the PictureBox shows all the splitted images.
The main problem with your current code is that the destination image (the image containing a slice of the original), is created once but painted many times.
Since the original image has transparent pixels, the result of the painting will be accumulated.
You can see the transparent sections overlapping.
It can be easily corrected, creating a new Bitmap for each slice of the original. You could also re-paint the same image with a transparent color, but this is faster.
In code, I'm assembling all the PictureBox controls that will receive the slices in one array, so you can assign the Image in the same loop that creates the Bitmaps.
You called the first PictureBox u1, so I'm following the same naming convention.
You will dispose of the Bitmap contained in the unitsimagearray when you don't need them anymore or the application closes.
Original Bitmap (.GIF):
Sliced images (2x). Anti-aliasing and transparency are preserved:
Private unitsimagearray(9) As Bitmap
Dim imagePath As String = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "images/u/image.gif")
Dim picBoxes() As PictureBox = {u1, u2, u3, u4, u5, u6, u7, u8, u9, u10}
Using sourceBitmap As Bitmap = Image.FromStream(New MemoryStream(File.ReadAllBytes(imagePath)))
For idx As Integer = 0 To picBoxes.Length - 1
Dim slice As Bitmap = New Bitmap(16, 16, PixelFormat.Format32bppArgb)
Using g As Graphics = Graphics.FromImage(slice)
Dim sourceRect As New Rectangle(idx * 19, 0, 16, 16)
Dim destinationRect As New Rectangle(0, 0, 16, 16)
g.DrawImage(sourceBitmap, destinationRect, sourceRect, GraphicsUnit.Pixel)
unitsimagearray(idx) = slice
picBoxes(idx).Image = unitsimagearray(idx)
End Using
Next
End Using

Drawing rect in picturebox not done to right scale for mouse

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

Saving a Drawing but not a Full Image in VB.net

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)

Graphics.RotateTransform() does not rotate my picture

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