how to merge two picturebox and save in a third picturebox

41 Views Asked by At

I'm here with another dilemma and I need your help. Let's go.... I have a custom control that you can draw on it, arrow, square and freehand, I use it together with a picturebox to capture the user's entire desktop. This process is now over, I can capture the work area, let the user select the area they want with my custom control, the user can draw freely on the control, however, when I save the image that the user selected, the drawings that the user made, does not come with the selected image. What I want to do is merge the drawings made in my control, together with the area selected by the user, and save or copy to the clipboard (this is not a problem), the problem is to merge the two information.

Example of capturing the work area with the drawings made: Capturing the work area This is the code for my custom control:

Imports System.ComponentModel
Imports System.Drawing.Drawing2D

Partial Public Class FrameControl
Inherits UserControl

<DefaultValue(True)>
Public Property CanMove As Boolean = True
<DefaultValue(False)>
Public Property DrawArrow As Boolean = False
<DefaultValue(False)>
Public Property DrawHand As Boolean = False
<DefaultValue(False)>
Public Property DrawRect As Boolean = False
Public Property DrawingcColor As Color = Color.LightGreen
Public Property PenSize As Single = 3.0F

Const WM_NCHITTEST As Integer = 132
Const WM_SETCURSOR As Integer = 32
Const WM_MOUSEMOVE As Integer = 32
Const WM_NCLBUTTONDBLCLK As Integer = 163
Private isMouseDown As Boolean

Private m_StartX, m_StartY, m_CurX, m_CurY As Single
Dim MyLineArrows As New List(Of LineList)

Private SelectedColor As Color = Color.LightGreen
Private drawingRects As List(Of DrawingRectangle) = New List(Of DrawingRectangle)()

Private linefreehand As New GraphicsPath()

Public Sub New()

    InitializeComponent()
    SetStyle(ControlStyles.SupportsTransparentBackColor, True)
    DoubleBuffered = True
    ResizeRedraw = True
    BackColor = Color.Transparent
    Timer1.Enabled = True
End Sub

Protected Overrides Sub OnPaint(e As PaintEventArgs)
    MyBase.OnPaint(e)
    Try
        Using p = New Pen(Color.White, 3)
            p.DashStyle = Drawing2D.DashStyle.Dash
            e.Graphics.DrawRectangle(p, 0, 0, Width - 1, Height - 1)
        End Using
    Catch ex As Exception
        Throw ex
    End Try
End Sub

Protected Overrides Sub WndProc(ByRef m As Message)
    Try
        Dim borderWidth As Integer = 10
        If m.Msg = WM_SETCURSOR Then
            If (m.LParam.ToInt32() And 65535) = 2 Then
                Cursor.Current = If(CanMove, Cursors.SizeAll, Cursors.Arrow)
                m.Result = CType(1, IntPtr)
                Return
            End If
        End If
        If m.Msg = WM_MOUSEMOVE Then
            If (m.LParam.ToInt32() And 65535) = 2 Then
                Cursor.Current = Cursors.Arrow
                m.Result = CType(1, IntPtr)
                Return
            End If
        End If
        If m.Msg = WM_NCLBUTTONDBLCLK Then
            m.Result = CType(1, IntPtr)
            Return
        End If
        MyBase.WndProc(m)
        If m.Msg = WM_NCHITTEST Then
            Dim pos = PointToClient(New Point(m.LParam.ToInt32() And 65535, m.LParam.ToInt32() >> 16))
            If (pos.X <= (ClientRectangle.Left + borderWidth)) AndAlso (pos.Y <= (ClientRectangle.Top + borderWidth)) Then
                m.Result = New IntPtr(13)
            Else
                If (pos.X >= (ClientRectangle.Right - borderWidth)) AndAlso (pos.Y <= (ClientRectangle.Top + borderWidth)) Then
                    m.Result = New IntPtr(14)
                Else
                    If (pos.X <= (ClientRectangle.Left + borderWidth)) AndAlso (pos.Y >= (ClientRectangle.Bottom - borderWidth)) Then
                        m.Result = New IntPtr(16)
                    Else
                        If (pos.X >= (ClientRectangle.Right - borderWidth)) AndAlso (pos.Y >= (ClientRectangle.Bottom - borderWidth)) Then
                            m.Result = New IntPtr(17)
                        Else
                            If pos.X <= (ClientRectangle.Left + borderWidth) Then
                                m.Result = New IntPtr(10)
                            Else
                                If pos.Y <= (ClientRectangle.Top + borderWidth) Then
                                    m.Result = New IntPtr(12)
                                Else
                                    If pos.X >= (ClientRectangle.Right - borderWidth) Then
                                        m.Result = New IntPtr(11)
                                    Else
                                        If pos.Y >= (ClientRectangle.Bottom - borderWidth) Then
                                            m.Result = New IntPtr(15)
                                        Else
                                            m.Result = (If(CanMove, New IntPtr(2), IntPtr.Zero))
                                        End If
                                    End If
                                End If
                            End If
                        End If
                    End If
                End If
            End If
        End If
    Catch ex As Exception
        Throw ex
    End Try

End Sub

Private Sub PictureBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDown
    Try
        isMouseDown = True
        If DrawArrow Then
            m_StartX = e.X
            m_StartY = e.Y
            m_CurX = e.X
            m_CurY = e.Y

        ElseIf DrawRect Then
            drawingRects.Add(New DrawingRectangle() With {
            .Location = e.Location,
            .Size = Size.Empty,
            .StartPosition = e.Location
            })

        ElseIf DrawHand Then

            linefreehand.StartFigure()

        End If

    Catch ex As Exception
        Throw ex
    End Try
   
End Sub

Private Sub PictureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseMove
    Try
        If isMouseDown Then
            If DrawArrow Then
                m_CurX = e.X
                m_CurY = e.Y

            ElseIf DrawRect Then

                Dim dr = drawingRects(drawingRects.Count - 1)

                If e.Y < dr.StartPosition.Y Then
                    dr.Location = New Point(dr.Rect.Location.X, e.Y)
                End If

                If e.X < dr.StartPosition.X Then
                    dr.Location = New Point(e.X, dr.Rect.Location.Y)
                End If

                dr.Size = New Size(Math.Abs(dr.StartPosition.X - e.X), Math.Abs(dr.StartPosition.Y - e.Y))
            ElseIf DrawHand Then
                linefreehand.AddLine(e.X, e.Y, e.X, e.Y)
            End If

            PictureBox1.Invalidate()
        End If
    Catch ex As Exception
        Throw ex
    End Try


End Sub

Private Sub FrameControl_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    UseDoubleBuffer()
End Sub

Private Sub PictureBox1_MouseUp(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseUp

    Try
        isMouseDown = False
        If DrawArrow Then
            Dim DrawLine As New LineList
            DrawLine.X1 = m_StartX
            DrawLine.Y1 = m_StartY
            DrawLine.X2 = m_CurX
            DrawLine.Y2 = m_CurY
            MyLineArrows.Add(DrawLine)


        ElseIf DrawRect Then
            Dim dr = drawingRects.Last()
        End If
    Catch ex As Exception
        Throw ex
    End Try



End Sub

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    Try
        If CanMove Then
            linefreehand.Reset()
            drawingRects.Clear()
            MyLineArrows.Clear()
            PictureBox1.Invalidate()
            PictureBox1.Visible = False
        Else
            PictureBox1.Invalidate()
            PictureBox1.Dock = DockStyle.Fill
            PictureBox1.BringToFront()
            PictureBox1.Visible = True

        End If
    Catch ex As Exception
        Throw ex
    End Try

End Sub

Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint

    DrawShapes(e.Graphics)

End Sub

Private Sub DrawShapes(ByVal g As Graphics)
    Try
        g.SmoothingMode = SmoothingMode.AntiAlias
        g.SmoothingMode = SmoothingMode.HighQuality
        g.SmoothingMode = SmoothingMode.HighSpeed

        For Each dr In drawingRects
            Using pen As Pen = New Pen(DrawingcColor, PenSize)
                g.DrawRectangle(pen, dr.Rect)
            End Using
        Next

        Dim x1, y1, x2, y2 As Integer
        Dim linecolor As Color = DrawingcColor
        Dim linepen As Single = PenSize

        For Each line In MyLineArrows
            x1 = line.X1 : x2 = line.X2
            y1 = line.Y1 : y2 = line.Y2
            Using Arrow1 As New Pen(linecolor, linepen)
                Arrow1.EndCap = LineCap.Custom
                Arrow1.CustomEndCap = New AdjustableArrowCap(2, 2, False)
                g.DrawLine(Arrow1, x1, y1, x2, y2)
            End Using
        Next

        If isMouseDown Then
            Using Arrow As Pen = New Pen(linecolor, linepen)
                Arrow.EndCap = LineCap.Custom
                Arrow.CustomEndCap = New AdjustableArrowCap(2, 2, False)
                g.DrawLine(Arrow, m_StartX, m_StartY, m_CurX, m_CurY)
            End Using
        End If
        
        Using handpen As Pen = New Pen(linecolor, linepen)
            g.DrawPath(handpen, linefreehand)
        End Using
    Catch ex As Exception
        Throw ex
    End Try


End Sub

Private Sub UseDoubleBuffer()
    Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or
                 ControlStyles.UserPaint Or
                 ControlStyles.OptimizedDoubleBuffer, True)
End Sub
End Class

This is the code I use to capture the desktop:

Imports System.Drawing.Drawing2D
Imports Controle_Customizado_PictureBox

Public Class frm_transp_form
Private imgInput As Image
Private EndLocation As Point
Private StartLocation As Point
Private rect As Rectangle
Private IsMouseDown As Boolean = False
Private ctrlResize As New FrameControl()


Private Sub frm_transp_form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Me.Size = New Size(Screen.AllScreens.Sum(Function(s As Screen) s.Bounds.Width), Screen.AllScreens.Max(Function(s As Screen) s.Bounds.Height))
    pb_screen.Dock = DockStyle.Fill
    Dim bmp As Bitmap = New Bitmap(Screen.AllScreens.Sum(Function(s As Screen) s.Bounds.Width), Screen.AllScreens.Max(Function(s As Screen) s.Bounds.Height))
    Using g As Graphics = Graphics.FromImage(bmp)
        g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
        g.CompositingQuality = Drawing2D.CompositingQuality.HighQuality
        g.CopyFromScreen(SystemInformation.VirtualScreen.X, SystemInformation.VirtualScreen.Y, 0, 0, SystemInformation.VirtualScreen.Size, CopyPixelOperation.SourceCopy)
        pb_screen.Image = bmp
    End Using
    Me.Location = New Point(0, 0)
    Me.Cursor = Cursors.Arrow
    ctrlResize.Size = New Size(0, 0)
    ctrlResize.Location = New Point(0, 0)
    ctrlResize.DrawingcColor = pb_color.BackColor
    pb_screen.Controls.Add(ctrlResize)

End Sub

Private Sub pb_screen_MouseDown(sender As Object, e As MouseEventArgs) Handles pb_screen.MouseDown
    IsMouseDown = True
    StartLocation = e.Location
    With ctrlResize
        .CanMove = True
        .DrawArrow = False
        .DrawHand = False
        .DrawRect = False
    End With
    pnl_menu.Visible = False
    Console.WriteLine("Startlocation: " & StartLocation.ToString)
End Sub

Private Sub pb_screen_MouseMove(sender As Object, e As MouseEventArgs) Handles pb_screen.MouseMove
    If IsMouseDown = True Then
        EndLocation = e.Location
        GetRectangle()
        Application.DoEvents()
        lbl_position.Text = rect.Width & "x" & rect.Height
        ctrlResize.Size = New Size(rect.Size)
        ctrlResize.Location = New Point((StartLocation.X), (StartLocation.Y))
        pnl_menu.Location = New Point(EndLocation.X + 5, EndLocation.Y - pnl_menu.Height)
        Application.DoEvents()
        pb_screen.Invalidate()
    End If
End Sub

Private Sub pb_screen_Paint(sender As Object, e As PaintEventArgs) Handles pb_screen.Paint
    e.Graphics.ExcludeClip(ctrlResize.Bounds)
    Using b = New SolidBrush(Color.FromArgb(100, Color.Black))
        e.Graphics.FillRectangle(b, pb_screen.ClientRectangle)
    End Using

End Sub

Private Sub pb_screen_MouseUp(sender As Object, e As MouseEventArgs) Handles pb_screen.MouseUp
    If IsMouseDown = True Then
        EndLocation = e.Location
        IsMouseDown = False

        Console.WriteLine("Retangulo: " & rect.Size.ToString)
        pnl_menu.Visible = True
    End If
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    End
End Sub

Private Function GetRectangle() As Rectangle
    rect = New Rectangle With {
        .X = Math.Min(StartLocation.X, EndLocation.X),
        .Y = Math.Min(StartLocation.Y, EndLocation.Y),
        .Width = Math.Abs(StartLocation.X - EndLocation.X),
        .Height = Math.Abs(StartLocation.Y - EndLocation.Y)
    }

    Return rect
End Function

Protected Overrides ReadOnly Property CreateParams As CreateParams
    Get
        Dim cp As CreateParams = MyBase.CreateParams
        cp.ExStyle = cp.ExStyle Or &H2000000
        Return cp
    End Get
End Property

Private Sub pb_color1_Click(sender As Object, e As EventArgs) Handles pb_color.Click
    'ColorDialog1.ShowDialog()
    If ColorDialog1.ShowDialog <> DialogResult.Cancel Then
        pb_color.BackColor = ColorDialog1.Color
        ctrlResize.DrawingcColor = ColorDialog1.Color
    End If
End Sub
Private Sub btn_arrow_Click(sender As Object, e As EventArgs) Handles btn_arrow.Click
    With ctrlResize
        .CanMove = False
        .DrawArrow = True
        .DrawHand = False
        .DrawRect = False
    End With
End Sub

Private Sub btn_freehand_Click(sender As Object, e As EventArgs) Handles btn_freehand.Click
    With ctrlResize
        .CanMove = False
        .DrawArrow = False
        .DrawHand = True
        .DrawRect = False
    End With
End Sub

Private Sub btn_rect_Click(sender As Object, e As EventArgs) Handles btn_rect.Click
    With ctrlResize
        .CanMove = False
        .DrawArrow = False
        .DrawHand = False
        .DrawRect = True
    End With
End Sub

Private Sub btn_copy_Click(sender As Object, e As EventArgs) Handles btn_copy.Click

    Dim selectedRectangle = New Rectangle(StartLocation.X, StartLocation.Y, ctrlResize.ClientRectangle.Width, ctrlResize.ClientRectangle.Height)
    Dim result = GetRectangeOnImage(pb_screen, selectedRectangle)
    Console.WriteLine("VALORES DO RETANGULO: " & selectedRectangle.ToString)
    Using bm = New Bitmap(CInt(result.Width), CInt(result.Height))

        Using g = Graphics.FromImage(bm)
            g.SmoothingMode = SmoothingMode.HighQuality
            g.CompositingQuality = CompositingQuality.HighQuality
            g.DrawImage(pb_screen.Image, 0, 0, result, GraphicsUnit.Pixel)
        End Using
        Dim img As Image = CType(bm.Clone(), Image)
        PictureBox1.Size = New Size(ctrlResize.Size)
        PictureBox1.Image = img
        PictureBox1.Visible = False
    End Using


End Sub
Public Function GetRectangeOnImage(ByVal p As PictureBox, ByVal selectionRect As Rectangle) As RectangleF
    Dim method = GetType(PictureBox).GetMethod("ImageRectangleFromSizeMode", System.Reflection.BindingFlags.NonPublic Or System.Reflection.BindingFlags.Instance)
    Dim imageRect = CType(method.Invoke(p, New Object() {p.SizeMode}), Rectangle)
    If p.Image Is Nothing Then Return selectionRect
    Dim cx = CSng(p.Image.Width) / CSng(imageRect.Width)
    Dim cy = CSng(p.Image.Height) / CSng(imageRect.Height)
    Dim r2 = Rectangle.Intersect(imageRect, selectionRect)
    r2.Offset(-imageRect.X, -imageRect.Y)
    Return New RectangleF(r2.X * cx, r2.Y * cy, r2.Width * cx, r2.Height * cy)
End Function
End Class

I accept suggestions in C# or VB.net, that's not a problem.

0

There are 0 best solutions below