Tác giả: 1045007
Cấp độ bài viết: nâng cao đối với những ai đang cơ bản.
Tóm tắt: các bạn thấy rằng ngày càng có nhiều các phần mềm về đồ họa, chỉnh sửa ảnh cho ra đời, chúng ta dễ dàng nhận ra các điểm chung giữa các sản phẩm đó là đầu vào là một tấm ảnh (hoặc không), đầu ra là một tấm ảnh đã được xử lý. Ở đây tôi không nói về vấn đề xử lý như thế hay các cấu trúc file ảnh ra sao vì khả năng tôi có giới hạn… Nhưng tôi sẽ hướng dẫn các bạn cách kết xuất ra một file ảnh do chính ta vẽ ra và hiểu được cách làm việc của các nét vẽ cơ bản (pixel, hình tròn, hình chữ nhật…).
Project:
Phần I: Vẽ Ảnh
Các khai báo cần thiết:
Mã: Chọn hết
- Dim bm As Bitmap 'biến bitmap lưu những gì ta đã vẽ
- Dim gp As Graphics 'biến chính để vẽ ảnh
- Dim draw As Boolean
- Dim line As String = "pixel" 'kiểu vẽ
- Dim c As Color = Color.Black 'Màu vẽ
Nếu chỉ để vẽ lên đối tượng ta chỉ cần viết code:
Mã: Chọn hết
- 'gan giá trị Graphics của đối tượng cho gp
- gp = Me.pic.CreateGraphics()
Vấn đề là khi runtime form luôn được vẽ lại khi có sự tác động, hậu quả là nó xóa luôn những gì ta đã vẽ lên.
Để khắc phục hiện tượng trên ta cần lưu những gì đã vẽ được vào một BitMap.
Mã: Chọn hết
- 'Thủ tục Chính để khởi tạo và thể hiện bm lên pictureBox
- Private Sub NewBitmap(ByVal w As Integer, ByVal h As Integer)
- bm = New Bitmap(w, h)
- Me.pic.Image = bm
- gp = Graphics.FromImage(bm)
- End Sub
1. Nét vẽ điểm (pixel)
Trong lớp Drawing2D không có sẵn hàm vẽ một điểm như là putpixel(x,y) trong ngôn ngữ C,C++…
Để vẽ một điểm ta có thể dùng:
Mã: Chọn hết
- bm.SetPixel(100, 100, Color.Black)
Mặc khắc có thể dùng hàm FillRectangle tô một hình chữ nhật có chiều dài và rộng = 1.
Mã: Chọn hết
- Private Sub pixel(ByVal e As System.Windows.Forms.MouseEventArgs)
- point = New Point(e.X, e.Y)
- gp.FillRectangle(New Pen(c).Brush, point.X, point.Y, 1, 1)
- End Sub
Bây giờ ta muốn khi rê chuột đến đâu, click chổ nào thì vẽ tại chổ đó, hoặc vừa kéo chuột vừa vẽ.
Vậy ta sẽ bắt 3 sự kiện chính: MouseDown, MouseUp, MouseMove của PictureBox cần vẽ lên.
Mã: Chọn hết
- Private Sub pic_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pic.MouseDown
- If e.Button = Windows.Forms.MouseButtons.Left Then
- draw = True
- MyDraw(e)
- End If
- End Sub
-
- Private Sub pic_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pic.MouseMove
- If draw = True Then
- MyDraw(e)
- End If
- End Sub
-
- Private Sub pic_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pic.MouseUp
- If draw = True Then
- draw = False
- End If
- End Sub
Còn đây là thủ tục MyDraw(e) chính gọi lại thủ tục pixel(e):
Mã: Chọn hết
- Private Sub MyDraw(ByVal e As System.Windows.Forms.MouseEventArgs)
- If line = "pixel" Then
- pixel(e)
- End If
- End Sub
Bây giờ thử được rồi đấy! kết quả mà ta mong đợi chắc là vầy:
Nhưng kết quả thực thì là vầy:
Lý do: tốc độ của vết chuột đi qua là rất cao mà sự kiện MouseMove không thể bắt kịp được tất cả các điểm đó nên kết quả cho ta thấy như hình trên.
Để khắc phục được nhược điểm, ta để ý rằng mỗi lần vết chuột đi qua nó cho ta một điểm, khoảng cách giữa 2 điểm liên tiếp vết chuột đi qua là tương đối gần do sự kiện MouseMove bắt được. Nên việc nối 2 điểm gần này lại với nhau là có thể tạm chấp nhận được.
Vậy việc cần làm là lưu các điểm vết chuột đi qua rồi nối chúng lại với nhau. Rất may lớp Drawing2D cho ta sẵn một kiểu GraphicsPath để lưu lại những gì ta vẽ lên:
Mã: Chọn hết
- 'Lưu lại đường thẳng được nối từ các điểm
- Dim mousepath As New GraphicsPath
- Dim point As Point 'Lưu các điểm đi qua
Thủ tục pixel và MyDraw được viết lại như sau:
Mã: Chọn hết
- Private Sub pixelDrag(ByVal e As System.Windows.Forms.MouseEventArgs)
- point = New Point(e.X, e.Y)
- mousepath.AddLine(point, point)
- gp.DrawPath(New Pen(c), mousepath)
- End Sub
- Private Sub MyDraw(ByVal e As System.Windows.Forms.MouseEventArgs)
- If line = "pixel" Then
- pixel(e)
- pixelDrag(e)
- Me.pic.Invalidate()
- End If
- End Sub
Nhớ lưu rồi thì phải xóa để còn sử dụng lại:
Mã: Chọn hết
- Private Sub pic_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pic.MouseUp
- If draw = True Then
- draw = False
- mousepath.Reset()
- End If
- End Sub
Và đây khuyến mãi thêm con chuột nhìn trông có vẽ pro hơn:
Mã: Chọn hết
- Public Function CreateCursor(ByVal bm As Bitmap, ByVal size As Size) As Cursor
- Try
- bm = New Bitmap(bm, size)
- bm.MakeTransparent()
- Return New Cursor(bm.GetHicon)
- Catch ex As Exception
- End Try
- Return Cursors.Default
- End Function
Xong! bây giờ thử nhé:
Mã: Chọn hết
- Private Sub f_paint_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
- NewBitmap(Me.pic.Width, Me.pic.Height)
- End Sub