Results 1 to 4 of 4

Thread: How to do some pretty neat GDI+

  1. #1
    Oranges!
    Join Date
    May 08
    Location
    British Columbia, Canada
    Posts
    2,781

    How to do some pretty neat GDI+

    All the following code assumes you have imported System.Drawing and System.Drawing.Imaging.
    I'll also be creating a custom control, a class, or a set of extensions to Graphics, so you don't have to remember all of this.

    1. Using a ColorMatrix.

    A ColorMatrix is an object you (can) use to change the values of the R, G, B, and A values in an image by multiplication. It's always an array of arrays of singles, 5x5. A diagonal line of 1 values is the default. Here's how you can use it to fade out a picture:
    Code:
    Dim g As Graphics = Me.CreateGraphics() 'Use whatever graphics source, even a bitmap.
    Dim bmp As New Bitmap(Image.FromFile("example.png"))
    Dim alpha As Single = 0.5! ' Draw at half transparency
    Dim arr()() As Single = { _
         New Single() {1,0,0,0,0}, _
         New Single() {0,1,0,0,0}, _
         New Single() {0,0,1,0,0}, _
         New Single() {0,0,0,alpha,0}, _
         New Single() {0,0,0,0,1} _
    }
    Dim cm As New ColorMatrix(arr)
    Dim iattr As New ImageAttributes()
    iattr.SetColorMatrix(cm)
    g.DrawImage(bmp,New Rectangle(0,0,bmp.Width,bmp.Height),0,0,bmp.Width,bmp.Height,GraphicsUnit.Pixel,iattr)
    Just put that in a loop that increments or decrements "alpha".
    Next: rotation, scaling, skewing, and flipping - all in one!
    Last edited by minitech; Jun 5th, 2010 at 12:22 PM.

  2. #2
    Oranges!
    Join Date
    May 08
    Location
    British Columbia, Canada
    Posts
    2,781

    Re: How to do some pretty neat GDI+

    (Okay, I actually had to look this one up again because I couldn't remember the order of the points array! It tuns out it's upper-left, upper-right, lower-left. I can't believe I couldn't remember! At least the ColorMatrix is easy enough.)

    2. Rotating, scaling, flipping, and skewing.

    In GDI+, you can do all of these transformations quickly and easily using an array of three points. You can also combine this with the "fading out" trick. The three points represent the upper-left corner, the upper-right corner, and the lower-left corner of the output parallelogram, respectively. Skewing can be done by moving the first two forwards or backwards, or the first and last up or down:
    Code:
    Dim rect As New Rectangle(20,20,100,100)
    Dim skewX As Integer = 20, skewY As Integer = 0
    Dim points() As Point = { _
         New Point(rect.X+skewX,rect.Y-skewY), _
         New Point(rect.Right+skewX,rect.Y), _
         New Point(rect.X,rect.Bottom-skewY) _
    }
    You can flip by just "flipping" the points with the center of the rectangle as an axis:
    Code:
    Dim flipX As Boolean = False
    Dim flipY As Boolean = False
    Dim points() As Point = {
         New Point(rect.X,rect.Y), _
         New Point(rect.Right,rect.Y), _
         New Point(rect.X,rect.Bottom) _
    }
    If flipX Then
         Dim tmp As Point = points(0)
         points(0) = points(1)
         points(1) = tmp
         tmp = points(2)
         tmp.X = rect.Right
         points(2) = tmp
    End If
    If flipY Then
         Dim tmp As Point = points(0)
         points(0) = points(2)
         points(2) = points(0)
         tmp = points(1)
         tmp.Y = rect.Bottom
         points(1) = tmp
    End If
    Last edited by minitech; Jun 5th, 2010 at 12:22 PM.

  3. #3
    Oranges!
    Join Date
    May 08
    Location
    British Columbia, Canada
    Posts
    2,781

    Re: How to do some pretty neat GDI+

    Rotation is more complicated, because it involves trigonometry. I struggled to figure this out for days, and only got as far as the upper-left hand corner, but I just Googled around for quite a while and found this:
    Code:
            Dim angle As Double = 30.0# 'Rotate 30 degrees counter-clockwise.
            ' Convert the angle in degrees.
            angle = angle / (180 / Math.PI)
            ' Find the position of (x1,y1) and (x2,y2).
            Dim x1 As Integer = CInt(rect.X + rect.Width * Math.Cos(angle))
            Dim y1 As Integer = CInt(rect.Y + rect.Width * Math.Sin(angle))
            Dim x2 As Integer = CInt(rect.X - rect.Height * Math.Sin(angle))
            Dim y2 As Integer = CInt(rect.Y + rect.Height * Math.Cos(angle))
    
            ' Create the points array.
            Dim points() As Point = {New Point(x, y), New Point(x1, y1), New Point(x2, _
                y2)}
    Finally, scaling is pretty much what you would expect and I won't post it here unless someone asks. If you want to combine the effects... try to figure it out. Just put the pieces of code together.

    Finally, how do you actually draw the image using the points array? Simple.
    Code:
    (graphics object).DrawImage((image object), points)
    Coming next: using GraphicsPaths, and an example: making a rounded rectangle.

  4. #4
    Oranges!
    Join Date
    May 08
    Location
    British Columbia, Canada
    Posts
    2,781

    Re: How to do some pretty neat GDI+

    3. Using the GraphicsPath

    This example requires you to import System.Drawing.Drawing2D.

    A GraphicsPath is a simple, but useful object that allows you to define a shape (or path of lines). It has AddXXX methods that are pretty much the same as the DrawXXX methods of the Graphics object, but you just don't define a color, there are no FillXXX, and there's no DrawImage. It's nice and simple. I assume that if you're using these examples, you probably already know something about GDI+ and the Graphics object. If you don't, I'm making another code/tutorial thing later for beginners. Anyways, since all the methods are self-explanatory, I won't go into detail. I'll just say "they're the same".

    You create a GraphicsPath object like so:
    Code:
    Dim gp As New GraphicsPath()
    You can also specify a FillMode, which is used for when your shapes cross themselves. Normally, (FillMode.Alternate) the pattern would be drawn in every second square... it's hard to explain without seeing it. The general way of doing things is to say "if you have shapes that cross themselves, use FillMode.Winding". It's the parameter of the constructor.

    You close your current shape using CloseFigure():
    Code:
    gp.CloseFigure()
    Even if you think you've manually closed your figure, it's a good idea to use this anyways.

    You can also create a Region object from a GraphicsPath (more on that later):
    Code:
    Dim r As New Region(gp)
    The GraphicsPath has many interesting properties, but they're a bit complicated and a bit of a waste of time to cover because they're rarely used. I'll cover them much later.

    Here's how you could create a rounded rectangle using the AddLine and AddArc methods of the GraphicsPath (you could even make an extension to the Graphics object, DrawRoundRectangle).
    This function accepts a location, size and a radius and returns a GraphicsPath:
    Code:
        Private Function GetRoundRect(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer, ByVal radius As Integer) As Drawing2D.GraphicsPath
            Dim gp As New GraphicsPath()
            Dim half As Integer = radius \ 2
    
            'Add across the top
            gp.AddLine(x + width - half, y, x + half, y)
            'Add the upper-left hand corner
            gp.AddArc(x, y, radius, radius, 270, -90)
            'Add across the left side
            gp.AddLine(x, y + half, x, y + height - half)
            'Add the lower-left hand corner
            gp.AddArc(x, y + height - 1 - radius, radius, radius, 180, -90)
            'Add across the bottom
            gp.AddLine(x + half, y + height - 1, x + width - half, y + height - 1)
            'Add the lower-right hand corner
            gp.AddArc(x + width - 1 - radius, y + height - 1 - radius, radius, radius, 90, -90)
            'Add across the right side
            gp.AddLine(x + width - 1, y + height - half, x + width - 1, y + half)
            'Add the upper-right hand corner
            gp.AddArc(x + width - 1 - radius, y, radius, radius, 0, -90)
    
            'Close the figure
            gp.CloseFigure()
    
            'Return the path
            Return gp
        End Function
    You may notice that I've made an odd choice of values for AddArc. This is important because you must draw the corners in the same "direction" as you're going. For example, if you draw a line from point a to point b, then point b to point d, then point a to e, you'll end up drawing another line from d to a, probably not what you want. You can see for yourself by changing the values in that code.
    Last edited by minitech; Jun 5th, 2010 at 12:37 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •