dcsimg

Inverting Image Colors in .NET

WEBINAR:
On-Demand

Desktop-as-a-Service Designed for Any Cloud ? Nutanix Frame


Introduction

I do not know how, but every now and then I return to graphics-oriented projects. Maybe it is because I always wanted to get into games programming, and still linger subconsciously to, but alas, here we are with a nice little project.

This article you will read (and hopefully enjoy) today deals with inverting colors in images. There are a few ways in .NET, which I will demonstrate. For the uninformed: Inverting colors means to take the opposite value of each color component and merge them to create the inverted color.

Let's create a small project.

Create a Windows Forms project in either VB.NET or C#. Once the project has loaded, add 4 buttons, 1 picturebox, and 1 OpenFileDialog control on the form. My design looks like Figure 1.

Design
Figure 1: Design

Double-click the button labeled "Load Image" and enter the following code:

C#

   private void btnLoadImage_Click(object sender, EventArgs e)
   {
      if (ofdLoad.ShowDialog() == DialogResult.OK)
      {
         try
         {
            Image imgSource = Image.FromFile(ofdLoad.FileName);
            picDisplay.Image = imgSource;
         }
         catch (Exception)
         {
            MessageBox.Show("Invalid image");
         }
      }
   }

VB.NET

   Private Sub btnLoadImage_Click(ByVal sender As Object, _
         ByVal e As EventArgs) Handles btnLoadImage.Click
      If ofdLoad.ShowDialog() = DialogResult.OK Then

         Try
            Dim imgSource As Image = _
               Image.FromFile(ofdLoad.FileName)
            picDisplay.Image = imgSource
         Catch __unusedException1__ As Exception
            MessageBox.Show("Invalid image")
         End Try
      End If
   End Sub

The Load Image button displays the OpenFileDialog, which allows you to browse for an image file. If a file has been selected, the image will be loaded inside the PictureBox.

Add the next code to invert an image using GDI+.

C#

   private static Image InvertGDI(Image imgSource)
   {
      Bitmap bmpDest = null;

      using (Bitmap bmpSource = new Bitmap(imgSource))
      {
         bmpDest = new Bitmap(bmpSource.Width, bmpSource.Height);

         for (int x = 0; x < bmpSource.Width; x++)
         {
            for (int y = 0; y < bmpSource.Height; y++)
            {

               Color clrPixel = bmpSource.GetPixel(x, y);

               clrPixel = Color.FromArgb(255 - clrPixel.R, 255 -
                  clrPixel.G, 255 - clrPixel.B);

               bmpDest.SetPixel(x, y, clrPixel);
            }
         }
      }

      return (Image)bmpDest;

   }
   private void btnGDI_Click(object sender, EventArgs e)
   {
      if (picDisplay.Image != null)
         picDisplay.Image = InvertGDI(picDisplay.Image);
   }

VB.NET

   Private Shared Function InvertGDI(ByVal imgSource As Image) _
         As Image
      Dim bmpDest As Bitmap = Nothing

      Using bmpSource As Bitmap = New Bitmap(imgSource)
         bmpDest = New Bitmap(bmpSource.Width, bmpSource.Height)

         For x As Integer = 0 To bmpSource.Width - 1

            For y As Integer = 0 To bmpSource.Height - 1
               Dim clrPixel As Color = bmpSource.GetPixel(x, y)
               clrPixel = Color.FromArgb(255 - clrPixel.R, 255 - _
                  clrPixel.G, 255 - clrPixel.B)
               bmpDest.SetPixel(x, y, clrPixel)
            Next
         Next
      End Using

      Return CType(bmpDest, Image)
   End Function
   Private Sub btnGDI_Click(ByVal sender As Object, ByVal e As _
         EventArgs) Handles btnGDI.Click
      If picDisplay.Image IsNot Nothing Then picDisplay.Image = _
         InvertGDI(picDisplay.Image)
   End Sub

Here, you loop through each image pixel and obtain its color. Then, you subtract 255 from the Red, Green, and Blue values of the pixel, thus producing the inverted look. Lastly, you replace the current pixel's color with the calculated color.

Add the next code to do inversion with the use of the ColorMatrix .NET object.

C#

   private static Image InvertColorMatrix(Image imgSource)
   {
      Bitmap bmpDest = new Bitmap(imgSource.Width,
         imgSource.Height);

      ColorMatrix clrMatrix = new ColorMatrix(new float[][]
         {
            new float[] {-1, 0, 0, 0, 0},
            new float[] {0, -1, 0, 0, 0},
            new float[] {0, 0, -1, 0, 0},
            new float[] {0, 0, 0, 1, 0},
            new float[] {1, 1, 1, 0, 1}
         });

      using (ImageAttributes attrImage = new ImageAttributes())
      {

         attrImage.SetColorMatrix(clrMatrix);

         using (Graphics g = Graphics.FromImage(bmpDest))
         {
            g.DrawImage(imgSource, new Rectangle(0, 0,
            imgSource.Width, imgSource.Height), 0, 0,
            imgSource.Width, imgSource.Height, GraphicsUnit.Pixel,
            attrImage);
         }
      }

      return bmpDest;
   }
   private void btnInvertMatrix_Click(object sender, EventArgs e)
   {
      if (picDisplay.Image != null)
         picDisplay.Image = InvertColorMatrix(picDisplay.Image);
   }

VB.NET

   Private Shared Function InvertColorMatrix(ByVal imgSource _
         As Image) As Image
      Dim bmpDest As Bitmap = New Bitmap(imgSource.Width, _
         imgSource.Height)
      Dim clrMatrix As ColorMatrix = New ColorMatrix(New 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, 1, 0}, New Single() {1, 1, 1, 0, 1}})

      Using attrImage As ImageAttributes = New ImageAttributes()
         attrImage.SetColorMatrix(clrMatrix)

         Using g As Graphics = Graphics.FromImage(bmpDest)
            g.DrawImage(imgSource, New Rectangle(0, 0, _
               imgSource.Width, imgSource.Height), 0, 0, _
               imgSource.Width, imgSource.Height, _
               GraphicsUnit.Pixel, attrImage)
         End Using
      End Using

      Return bmpDest
   End Function

   Private Sub btnInvertMatrix_Click(ByVal sender As Object, _
         ByVal e As EventArgs) Handles btnInvertMatrix.Click
      If picDisplay.Image IsNot Nothing Then picDisplay.Image = _
         InvertColorMatrix(picDisplay.Image)
   End Sub

For C#, add the unsafe method. VB.NET does not support unsafe code. For the Unsafe code to work with C#, click Project, Properties, Build, and then check the Allow Unsafe Code checkbox. Add the next code:

C#

   private static Image InvertUnsafe(Image imgSource)
   {

      Bitmap bmpDest = new Bitmap(imgSource);

      BitmapData bmpSource = bmpDest.LockBits(new Rectangle(0, 0,
         bmpDest.Width, bmpDest.Height), ImageLockMode.ReadWrite,
         PixelFormat.Format32bppArgb);

      int intStride = bmpSource.Stride;
      IntPtr iptrScan0 = bmpSource.Scan0;

      unsafe   // Project, Properties, Build, Allow Unsafe Code
      {

         byte* p = (byte*)(void*)iptrScan0;

         int intOffset = intStride - bmpDest.Width * 4;
         int intWidth = bmpDest.Width;

         for (int y = 0; y < bmpDest.Height; y++)
         {
            for (int x = 0; x < intWidth; x++)
            {
               p[0] = (byte)(255 - p[0]);
               p[1] = (byte)(255 - p[1]);
               p[2] = (byte)(255 - p[2]);

               p += 4;
            }
            p += intOffset;
         }
      }

      bmpDest.UnlockBits(bmpSource);

      return (Image)bmpDest;
   }




   private void btnUnsafe_Click(object sender, EventArgs e)
   {
      if (picDisplay.Image != null)
         picDisplay.Image = InvertUnsafe(picDisplay.Image);
   }

The unsafe method converts each pixel byte for byte. Supposedly, it uses less memory, but with this little example the effect is lost.

Figure 2 displays a normal picture. Figure 3 displays the inverted version of the picture.

Loaded Picture
Figure 2: Loaded Picture

Inverted
Figure 3: Inverted

Conclusion

As you can see, there are always more than one option to achieve a goal. With graphics, there is a plethora of options for dealing with color. Maybe in another article, I can play around more, but, until then, code safely!



About the Author

Hannes DuPreez

Hannes du Preez is a self-taught developer who started learning to program in the days of QBasic. He has written several articles over the years detailing his programming quests and adventures. .NET is his second love, just after his wife and kid. He has always been an avid supporter of .NET since the beginning and is an expert in VB and C#. He was given the Microsoft Most Valuable Professional Award for .NET (2008–2017). He has worked as a moderator and an article reviewer on online forums and currently works as a C# developer and writes articles for CodeGuru.com, Developer.com, DevX.com, and the Database journal. His first book, Visual Studio 2019 in Depth... is currently on sale on Amazon.com.

Related Articles

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date