65.9K
CodeProject is changing. Read more.
Home

Image Color Picker

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2 votes)

Sep 9, 2011

CPOL

1 min read

viewsIcon

30792

downloadIcon

904

Turn an image to a ColorPicker.

Introduction

This article demonstrates how to turn an image to a simple color picker.

Background

Actually, there is a lot of custom color pickers in the internet, in MSDN, and also here at The Code Project. You will find a lot of similar articles talking about custom color pickers but I never saw an article discuss how to turn an image/bitmap to a color picker, so I decided to give it a try, and here it is. I really hope it will be helpful for fellow CodeProject members.

How to Get Color from a Bitmap

To get a pixel color from an image/bitmap, simply use the Bitmap.GetPixel method which is a part of the Bitmap class, and to do that, you need to define an Image object, then pass the x and y coordinates of the pixel color to the GetPixel method, and you will get the color of the pixel. The code looks something like:

' Create a Bitmap object 
Dim bmp As New Bitmap("Grapes.jpg")

' Define the x-coordinate of the pixel to retrieve. 
Dim x As Integer = 20

' Define the x-coordinate of the pixel to retrieve. 
Dim y As Integer = 20

' Define the color and pass x, y coordinates to GetPixel Method
Dim pixelColor As Color = bmp.GetPixel(x, y)

Image Color Picker Control

The idea behind the image color picker is to pass a byRef point to the MouseDown and MouseMove events and get the mouse location, then pass the same point to a private method to get the color at that point.

ImageColorPicker Files

  • ImageColorPicker.vb: Extends the Windows.Forms.Control class.

Control Properties

  • Image: used to set the user picked image/bitmap.
  • Public Property Image As Bitmap
        Get
            Return Me.originalBitmap
        End Get
        Set(ByVal value As Bitmap)
            Me.originalBitmap = value
            Me.DrawImage()
            Me.Invalidate()
        End Set
    End Property
  • Color: gets the color selected.
  • Public Property Color As Color
        Get
            Return Me.selectedColor
        End Get
        Set(ByVal value As Color)
            Me.selectedColor = value
            Me.PixelColorToPoint()
            Me.DrawImage()
            MyBase.Invalidate()
        End Set
    End Property

Control Events

  • ColorChanged: occurs when the image pixel color changes, it also represents the control's DefaultEvent.
  • ''' <summary>
    ''' occurs when pixel color changed
    ''' </summary>
    ''' <remarks></remarks>
    Public Custom Event ColorChanged As EventHandler
        AddHandler(ByVal value As EventHandler)
            Me.Events.AddHandler("ColorChangedEvent", value)
        End AddHandler
    
        RemoveHandler(ByVal value As EventHandler)
            Me.Events.RemoveHandler("ColorChangedEvent", value)
        End RemoveHandler
    
        RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
            CType(Me.Events("ColorChangedEvent"), EventHandler).Invoke(sender, e)
        End RaiseEvent
    End Event

Control Methods

#Region " Methods"

Private Sub DrawImage()
    If MyBase.Width > 0 AndAlso Me.originalBitmap IsNot Nothing Then
        Me.pickerBitmap = New Bitmap(MyBase.ClientRectangle.Width - _
            (Me.controlBorder * 2), MyBase.ClientRectangle.Height - _
            (Me.controlBorder * 2))
        Dim g As Graphics = Graphics.FromImage(Me.pickerBitmap)
        Dim mode As SmoothingMode = g.SmoothingMode
        Dim rect As New Rectangle(0, 0, Me.pickerBitmap.Width, _
                    Me.pickerBitmap.Height)
        g.DrawImage(Me.originalBitmap, rect)
        g.SmoothingMode = mode
        g.Dispose()
    End If
End Sub

''' <summary>
''' Get pixel color point
''' </summary>
''' <param name="pt">pixel point</param>
''' <param name="pixelColor">pixel color</param>
''' <param name="w">width</param>
''' <param name="h">height</param>
''' <remarks></remarks>
Private Sub PixelColorToPoint(ByRef pt As Point, ByVal pixelColor _
            As Color, ByVal w As Integer, ByVal h As Integer)
    pt.X = ((255 - pixelColor.GetBrightness()) * w) / 255
    pt.Y = ((255 - pixelColor.GetSaturation()) * h) / 255
End Sub

Private Sub PixelColorToPoint()
    PixelColorToPoint(New Point(Me.selectedPoint.X, Me.selectedPoint.Y), _
      Me.selectedColor, MyBase.Width - (2 * Me.controlBorder), _
      MyBase.Height - (Me.controlBorder))
End Sub

''' <summary>
''' check if pixel color point is within the boundary
''' of specified width and height and if not reset its value
''' </summary>
''' <param name="pt">point</param>
''' <param name="w">width</param>
''' <param name="h">height</param>
''' <param name="border">control border</param>
''' <remarks></remarks>
Private Sub CheckPixelColorPoint(ByRef pt As Point, ByVal w As _
            Integer, ByVal h As Integer, ByVal border As Integer)
    If (pt.X - border) < 0 Then
        pt.X = border
    End If
    If pt.X > ((w - border) - 1) Then
        pt.X = (w - border) - 1
    End If

    If (pt.Y - border) < 0 Then
        pt.Y = border
    End If
    If pt.Y > ((h - border) - 1) Then
        pt.Y = (h - border) - 1
    End If
End Sub

''' <summary>
''' check if pixel color point is within the boundary
''' of the control width and height, if not reset its value
''' </summary>
''' <param name="pt"></param>
''' <remarks></remarks>
Private Sub CheckPixelColorPoint(ByRef pt As Point)
    Me.CheckPixelColorPoint(pt, MyBase.ClientRectangle.Width, _
            MyBase.ClientRectangle.Height, controlBorder)
End Sub

''' <summary>
''' 
''' </summary>
''' <param name="pt"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Function HitTestPixelPoint(ByVal pt As Point) As Boolean
    Me.CheckPixelColorPoint(pt)
    If Me.originalBitmap IsNot Nothing Then
        If (((pt.X - Me.controlBorder) >= 0) AndAlso _
             ((pt.X - Me.controlBorder) < Me.pickerBitmap.Width)) _
               AndAlso (((pt.Y - Me.controlBorder) >= 0) _
               AndAlso ((pt.Y - Me.controlBorder) < Me.pickerBitmap.Height)) Then
            Dim pixelcolor As Color = _
                Me.pickerBitmap.GetPixel((pt.X - Me.controlBorder), _
                (pt.Y - Me.controlBorder))
            If pixelcolor.A > 0 Then
                Me.selectedColor = pixelcolor
                Me.selectedPoint.X = pt.X - Me.controlBorder
                Me.selectedPoint.Y = pt.Y - Me.controlBorder
                Return True
            End If
        End If
    End If
    Return False
End Function

Protected Sub OnColorChanged(ByVal e As EventArgs)
    Dim colorChangedHandler As EventHandler = _
         CType(Me.Events("ColorChangedEvent"), EventHandler)
    If (colorChangedHandler IsNot Nothing) Then
        colorChangedHandler.Invoke(Me, e)
    End If
End Sub

Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
    MyBase.OnMouseDown(e)
    If Me.HitTestPixelPoint(e.Location) Then
        If Not Me.Focused Then
            MyBase.Focus()
        End If
        MyBase.Invalidate()
        Me.OnColorChanged(New EventArgs())
    End If
End Sub

Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
    MyBase.OnMouseMove(e)
    If (e.Button = Windows.Forms.MouseButtons.Left) _
            AndAlso Me.HitTestPixelPoint(e.Location) Then
        MyBase.Invalidate()
        Me.OnColorChanged(New EventArgs())
    End If

End Sub

Protected Overrides Sub OnEnter(ByVal e As System.EventArgs)
    MyBase.OnEnter(e)
    MyBase.Invalidate()
End Sub

Protected Overrides Sub OnLeave(ByVal e As System.EventArgs)
    MyBase.OnLeave(e)
    MyBase.Invalidate()
End Sub

Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
    Me.DrawImage()
    MyBase.OnSizeChanged(e)
End Sub

Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
    Me.DrawImage()
    MyBase.OnResize(e)
End Sub

Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
    MyBase.OnPaint(e)

    Dim mode As SmoothingMode = e.Graphics.SmoothingMode
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias

    If Me.pickerBitmap IsNot Nothing Then
        Using lgb As New LinearGradientBrush(Me.ClientRectangle, _
                  Color.Black, Color.FromArgb(200, Color.Black), 90, True)
            e.Graphics.FillRectangle(lgb, Me.ClientRectangle)
        End Using
        e.Graphics.DrawImage(Me.pickerBitmap, Me.controlBorder, Me.controlBorder)
    End If

    If Not Me.DesignMode Then
        Dim r As New Rectangle((Me.controlBorder + Me.selectedPoint.X) - 5, _
             (Me.controlBorder + Me.selectedPoint.Y) - 5, 10, 10)
        Using sb As New SolidBrush(Me.selectedColor)
            e.Graphics.FillRectangle(sb, r)
            e.Graphics.DrawRectangle(Pens.White, r)
            r.Inflate(1, 1)
            e.Graphics.DrawRectangle(Pens.Black, r)
        End Using
    End If

End Sub

#End Region

Using the Control

Build the ImageColorPicker class, drag it to your form, then use it as follows:

Public Class Form1

    Private selectedColor As Color

    Private Sub ImagColorPicker1_ColorChanged(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles ImagColorPicker1.ColorChanged
        selectedColor = Me.ImagColorPicker1.Color
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        e.Graphics.FillRectangle(New SolidBrush(Me.selectedColor), _
                   New Rectangle(0, 0, 30, 30))
    End Sub

End Class