Search code examples
vb.netsystem.drawingmousemove

How to avoid overlapping of opaque rectangles on graphics.drawrectangle()?


After a bunch of fail on Google searches finally I thought for asking for experts here to help me at this problem as this site always helped me.

What I want?

I want to create a Highlighter for my some kind of drawing application. I want this to be similar to the highlighter you can see on the Windows Snipping Tool.

What is my problem?

The problem is that although I can draw the semitransparent or opaque rectangles using the code, gfx.FillRectangle(New SolidBrush(Color.FromArgb(100, Colors.GreenYellow)), x, y, width, height), but if I draw another rectangle overlapping any previous rectangles the colors gets darker and reduces the transparency of the rectangles where they overlapped.

Code:

Public Class Form1
    Dim drag As Boolean
    Dim mouseX, mouseY As Integer
    Dim prev As Point
    Dim initi As Point
    Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        Dim grx As Graphics = Panel1.CreateGraphics
        grx.DrawString("+", New Font("Arial", 144, FontStyle.Regular), New SolidBrush(Color.FromArgb(100, Color.GreenYellow)), New Point(200, 200))
    End Sub

    Private Sub Panel1_MouseDown(sender As Object, e As MouseEventArgs) Handles Panel1.MouseDown
        drag = True
        mouseX = MousePosition.X - Me.Left - 8
        mouseY = MousePosition.Y - Me.Top - 34
        initi = New Point(mouseX, mouseY)
    End Sub

    Private Sub Panel1_MouseMove(sender As Object, e As MouseEventArgs) Handles Panel1.MouseMove
        If drag Then
            mouseX = MousePosition.X - Me.Left - 8
            mouseY = MousePosition.Y - Me.Top - 34
        End If
    End Sub

    Private Sub Panel1_MouseUp(sender As Object, e As MouseEventArgs) Handles Panel1.MouseUp
        drag = False
        prev = New Point(0, 0)
        Dim grx As Graphics = Panel1.CreateGraphics
        grx.FillRectangle(New SolidBrush(Color.FromArgb(100, Color.GreenYellow)), initi.X, initi.Y, (mouseX - initi.X), (mouseY - initi.Y))

    End Sub
End Class

Screenshot of the application (Showing the problem)

  • Left "+" is the one that I want to draw.
  • Right "+" is the one that I get when I draw.

enter image description here


Solution

  • Well. I've never really used graphics, but the only thing I could come up with is to create a list of the highlights to be drawn and then each time the mouse_up event fires, create a new bitmap, draw each rectangle pixel by pixel, with semi-transparent pixels to the bitmap and then draw the resulting bitmap to the panel using the panel's paint event handler that fires when you refresh the panel. This seems the built-in alpha blending that .net does automatically.

    Add this to your form's variable declarations

    Dim highlightsList As New List(Of Rectangle)
    Dim bmp1 As Bitmap
    

    alter your form_shown event to

    Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
        bmp1 = New Bitmap(Panel1.Width, Panel1.Height)
    End Sub 
    

    add this sub which handles the addition of the rectangles to the list and the creation of the bitmap

    Private Sub addRectangle(gr As Graphics, x As Integer, y As Integer, v1 As Integer, v2 As Integer)
        Dim newRectangle As New Rectangle(x, y, v1, v2)
        highlightsList.Add(newRectangle)
        Using G As Graphics = Graphics.FromImage(bmp1)
            G.Clear(Color.White)
        End Using
        bmp1.MakeTransparent(Color.White)
        For Each rect As Rectangle In highlightsList
            For i As Integer = rect.X To rect.X + rect.Width - 1
                For j As Integer = rect.Y To rect.Y + rect.Height - 1
                    bmp1.SetPixel(i, j, Color.FromArgb(100, Color.GreenYellow))
                Next
            Next
        Next
        Panel1.Refresh()
    End Sub
    

    add a handler for the panel's paint event so that when you refresh the panel, the bitmap is drawn onto it

    Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
        e.Graphics.DrawImage(bmp1, 0, 0)
    End Sub
    

    and change your mouse_up event to use the above sub to do the drawing

    Private Sub Panel1_MouseUp(sender As Object, e As MouseEventArgs) Handles Panel1.MouseUp
        drag = False
        prev = New Point(0, 0)
        Dim grx As Graphics = Panel1.CreateGraphics
        'grx.Clear(Panel1.BackColor)
        addRectangle(grx, initi.X, initi.Y, (mouseX - initi.X), (mouseY - initi.Y))
    End Sub
    

    This seems to work, but if you're using the panel for anything other that showing highlights, it may not work as expected.