Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Articles
Search: 

Special effects on images with new GDIPlus-X classes - Part 1
Cesar Chalom, August 1, 2006
Following previous articles on the topic, this one describes how to create some cool effects on images with the help of color matrices. Sometimes we may need to make some adjustments in the colors of an image, like to change Saturation, Contrast, Brightness, Hue, convert to Grayscales, to increase o...
Summary
Following previous articles on the topic, this one describes how to create some cool effects on images with the help of color matrices. Sometimes we may need to make some adjustments in the colors of an image, like to change Saturation, Contrast, Brightness, Hue, convert to Grayscales, to increase or decrease a specific, to make Gamma adjustments, colors, etc. And again GDIPlus is the perfect tool for this purpose. The article talks about the use of the new GDIPlus-X library for Visual FoxPro to better achieve some special effects.
Description
Sometimes we may need to make some adjustments in the colors of an image, like to change Saturation, Contrast, Brightness, Hue, convert to Grayscales, to increase or decrease a specific, to make Gamma adjustments, colors, etc. And again GDIPlus is the perfect tool for this purpose.

New GDIPlus-X library for Visual FoxPro

The most appropriate instrument for this task is the new GDIPlus-X library from VFP-X project. Since the _gdiplus.vcx class that ships with VFP9 is incomplete, for many tasks we had to make direct API calls to gdiplus.dll. Although it’s still an unfinished work, the actual ALPHA version of the GDIPlus-X library is very stable and already reliable to do most GDI+ tasks.

In the last months I've been doing some extensive tests and creating some examples using the new library. I must tell that these classes are terrific. This library is already making my life with GDI+ a lot easier. The more I use it, the more I like it.

Craig Boyd (http://www.sweetpotatosoftware.com/SPSBlog/default.aspx) said in one of his blog posts: "The GDIPlus-X library is a pure Visual FoxPro reproduction of the Drawing related namespaces in .NET. We've coded well over 44,000 lines of VFP code and the library consists of 80+ classes now. At nearly 95% complete. It is safe to say that no other library on the planet gives Visual FoxPro developers the functionality and power that this one does when working with GDI+". The project manager is Bo Durban (www.moxiedata.com). He and our "community-president" Craig Boyd did a difficult, amazing and very inventive work.

Start downloading the latest stable release from codeplex, even if you already have one version from http://www.codeplex.com/Release/ProjectReleases.aspx?ProjectName=VFPX .

Working with the images

The simplest way to do that would be to "read" every single pixel of the image, extract the 4 integer ARGB values (Alpha, Red, Green and Blue), apply the adjustments to the color and then "redraw" every pixel.

The code shows how we can convert an Image to Grayscale pixel by pixel. The effective luminance of a pixel is calculated with the following formula: C = 0.3 RED + 0.59 GREEN + 0.11 BLUE. You may also use this simplified formula : NewColor = (red + Green + Blue) / 3 , and apply "newColor" to each RGB color component.

Before you run this code, make sure to choose a small picture, because this process is really very slow. Below is the new VFP-X logo and its grayscale version:

IMPORTANT:

When you run this sample, "LOCFILE()" will search the "system.vcx" file, that is in the folder source. If you add the "source" folder to your path, you won't be asked to find this file.

** convert an Image to GreyScale pixel by pixel
**
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx"))) 

WITH _SCREEN.System.Drawing
	&& Load the image
	LOCAL loBitmap AS xfcBitmap
	LOCAL loNewBitmap AS xfcBitmap

	loBitmap = .Bitmap.New(GETPICT())

	LOCAL lnWidth, lnHeight, X, Y, lnLuma 
	lnWidth = loBitmap.Width
	lnHeight = loBitmap.Height 

	loNewBitmap = .Bitmap.New(lnWidth, lnHeight)

	LOCAL loColor AS xfcColor

	WAIT WINDOW "Converting Image... Please wait..." NOWAIT 
	FOR Y = 0 TO lnHeight - 1 
		FOR x = 0 TO lnWidth - 1 
			loColor = loBitmap.GetPixel(X,Y)
			lnLuma = INT(loColor.R * 0.3 + loColor.G * 0.59 + loColor.B * 0.11)
			loNewBitmap.SetPixel(x, y, .Color.FromRgb(RGB(lnLuma,;
                         lnLuma, lnLuma)))
		NEXT 
	NEXT 
	loNewBitmap.Save("c:\_GreyScale.bmp", .Imaging.ImageFormat.Bmp)
	WAIT CLEAR 
ENDWITH
RETURN
GDI+ brings us with the possibility to apply changes to image colors in a really fast way. But to do that, we need to use the ImageAttributes class. According to Microsoft, "An ImageAttributes object contains information about how bitmap and metafile colors are manipulated during rendering. An ImageAttributes object maintains several color-adjustment settings, including color-adjustment matrices, grayscale-adjustment matrices, gamma-correction values, color-map tables, and color-threshold values."

My favorite method in the ImageAttributes class is the "SetColorMatrix".

According to MSDN, "A 5x5 color matrix is a homogeneous matrix for a 4-space transformation. The element in the fifth row and fifth column of a 5x5 homogeneous matrix must be 1, and all of the other elements in the fifth column must be 0. Color matrices are used to transform color vectors. The first four components of a color vector hold the red, green, blue, and alpha components (in that order) of a color. The fifth component of a color vector is always 1."

Fortunately, MSDN brings to us some really cool examples. Enter www.msdn.com and search for "Gdiplus ColorMatrix". From now on, to apply these examples will be a simple task. MSDN provides many code examples for GDI+, mostly for VisualBasic, as you can see in this link: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdrawingimagingcolormatrixclasstopic.asp

Below is the sample code that uses color matrices provided by MSDN for VisualBasic:

Public Sub SetColorMatrixExample(e As PaintEventArgs)
' Create a rectangle image with all colors set to 128 (medium
' gray).
Dim myBitmap As New Bitmap(50, 50, PixelFormat.Format32bppArgb)
Dim g As Graphics = Graphics.FromImage(myBitmap)
g.FillRectangle(New SolidBrush(Color.FromArgb(255, 128, 128, _
128)), New Rectangle(0, 0, 50, 50))
myBitmap.Save("Rectangle1.jpg")
' Open an Image file and draw it to the screen.
Dim myImage As Image = Image.FromFile("Rectangle1.jpg")
e.Graphics.DrawImage(myImage, 20, 20)
' Initialize the color matrix.
Dim myColorMatrix As New ColorMatrix()
myColorMatrix.Matrix00 = 1.75F ' Red
myColorMatrix.Matrix11 = 1F ' Green
myColorMatrix.Matrix22 = 1F ' Blue
myColorMatrix.Matrix33 = 1F ' alpha
myColorMatrix.Matrix44 = 1F ' w
' Create an ImageAttributes object and set the color matrix.
Dim imageAttr As New ImageAttributes()
imageAttr.SetColorMatrix(myColorMatrix)
' Draw the image using the color matrix.
Dim rect As New Rectangle(100, 20, 200, 200)
e.Graphics.DrawImage(myImage, rect, 0, 0, 200, 200, _
GraphicsUnit.Pixel, imageAttr) ' Image
End Sub 'SetColorMatrixExample
This is the same example 100% in VFP, using the VFP-X GDIPlus-X library
** Converted to GDIPlusX for VFP from .NET help:
** http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/
** frlrfsystemdrawingimagingcolormatrixclasstopic.asp

** Creates a rectangle image that has all the color values set to 128, producing
**   a rectangle that is filled with a solid medium gray color. 
** The code then draws this rectangle image to the screen. 
** Creates a ColorMatrix object and sets its Matrix00 location to 1.75, 
**   which emphasizes the red component of the image. 
** Creates an ImageAttributes object and calls the SetColorMatrix method. 
** Draws the image (a second rectangle) to the screen using the ColorMatrix
**   object just set in the ImageAttributes object. 

_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx"))) 

WITH _SCREEN.System.Drawing

*!*	Create a rectangle image with all colors set to 128 (medium gray).
LOCAL loMyBitmap AS xfcBitmap
loMyBitmap = .Bitmap.New(50, 50, .Imaging.PixelFormat.Format32bppArgb)

LOCAL loGfx AS xfcGraphics
loGfx = .Graphics.FromImage(myBitmap)
loGfx.FillRectangle( .SolidBrush.New(.Color.FromArgb(255,128,128,128)), ;
	.Rectangle.New(0, 0, 50, 50))
loMyBitmap.Save("c:\Rectangle1.jpg")


*!* Open an Image file and draw it to the screen.
LOCAL loMyImage AS xfcImage
loMyImage = .Image.FromFile("c:\Rectangle1.jpg")
LOCAL loImgGfx AS xfcGraphics
loImgGfx = .Graphics.FromHwnd(_Screen.HWnd)
loImgGfx.DrawImage(loMyImage, 20, 20)

*!* Initialize the color matrix.
LOCAL lomyColorMatrix AS xfcColorMatrix
loMyColorMatrix = .Imaging.ColorMatrix.New()
loMyColorMatrix.Matrix00 = 1.75 && Red
loMyColorMatrix.Matrix11 = 1    && Green
loMyColorMatrix.Matrix22 = 1    && Blue
loMyColorMatrix.Matrix33 = 1    && alpha
loMyColorMatrix.Matrix44 = 1    && w

*!* Create an ImageAttributes object and set the color matrix.
LOCAL loImageAttr AS xfcImageAttributes
loImageAttr = .Imaging.ImageAttributes.New()
loImageAttr.SetColorMatrix(loMyColorMatrix)

*!* Draw the image using the color matrix.
LOCAL loRect as xfcRectangle
loRect = .Rectangle.New(100, 20, 200, 200)
loImgGfx.DrawImage(loMyImage, loRect, 0, 0, 200, 200, ;
	.GraphicsUnit.Pixel, loImageAttr) && Image

ENDWITH 

RETURN
This is a 5x5 a color matrix:

Matrix Description
C11 C12 C13 C14 C15 component R - Red
C21 C22 C23 C24 C25 component G - Green
C31 C32 C33 C34 C35 component B - Blue
C41 C42 C43 C44 C45 component A - Alpha (transparency)
C51 C52 C53 C54 C55 component Brightness

In this matrix, I'll classify some important positions:

Positions Description Notes
C11 - C22 - C33 Control the contrast of a color Position 11 = Red, 22 = Green, 33 = Blue
1 means no change
0 means to eliminate the component
C44 Control of opacity/alpha
C51 - C52 - C53 Control the brightness of a component 0 means no change
For example: if you put 0.75 in position C51, the red component of each pixel in the image will be increased in 0.75
C54 Control the brightness of the OPACITY/ALPHA component (0 means no change)

You can get some very interesting effects applying color matrices. So, let's have some fun, playing with GDI+ and the new library. The example below converts an image to grayscale in a flash. For these examples, I'll use the Level Extreme logo:

_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx","vcx"))) 
LOCAL loBitmap AS xfcBitmap
LOCAL loNewBitmap AS xfcBitmap
LOCAL loGfx AS xfcGraphics
LOCAL loAttr AS xfcImageAttributes
LOCAL loClrMatrix AS xfcColorMatrix
LOCAL loRect AS xfcRectangle 

WITH _SCREEN.System.Drawing 
loBitmap = .Bitmap.New(GETPICT())
loNewBitmap = .Bitmap.New(loBitmap.Width, loBitmap.Height) 
* Create a Graphics object to be able to draw in the bitmap
loGfx = .Graphics.FromImage(loNewBitmap) 
loClrMatrix = .Imaging.ColorMatrix.New( ;
	0.33, 0.33, 0.33, 0.0, 0.0, ;
	0.33, 0.33, 0.33, 0.0, 0.0, ;
	0.33, 0.33, 0.33, 0.0, 0.0, ;
	0.0 , 0.0 , 0.0 , 1.0, 0.0, ;
	0.0 , 0.0 , 0.0 , 0.0, 1.0) 
loAttr = .Imaging.ImageAttributes.New()
loAttr.SetColorMatrix(loClrMatrix)
loRect = .Rectangle.New(0,0,loBitmap.Width, loBitmap.Height)
loGfx.DrawImage(loBitmap, loRect, loRect, .GraphicsUnit.Pixel, loAttr) 
loNewBitmap.Save("c:\NewImage.jpg", .Imaging.ImageFormat.Jpeg)
ENDWITH
RETURN
Below are some other examples. To see the results, just change the contents of the object loClrMatrix to these ones:

Matrix1 - Obtain Negative of an Image

loClrMatrix = .Imaging.ColorMatrix.New( ;
   -1,  0,  0,  0,  0, ;
   0 , -1,  0,  0,  0, ;
   0 ,  0, -1,  0,  0, ;
   0 ,  0,  0,  1,  0, ;
   1 ,  1,  1,  0,  1)
Matrix2 - Obtain just the RED components of an Image

loClrMatrix = .Imaging.ColorMatrix.New( ;
   1 ,  0,  0,  0,  0, ;
   0 ,  0,  0,  0,  0, ;
   0 ,  0,  0,  0,  0, ;
   0 ,  0,  0,  1,  0, ;
   0 ,  0,  0,  0,  1)
Matrix3 - REMOVE the BLUE components of an Image

loClrMatrix = .Imaging.ColorMatrix.New( ;
   1 ,  0,  0,  0,  0, ;
   0 ,  1,  0,  0,  0, ;
   0 ,  0,  0,  0,  0, ;
   0 ,  0,  0,  1,  0, ;
   0 ,  0,  0,  0,  1)
Matrix4 - Scales the blue component of each pixel in the image by a factor of 2

loClrMatrix = .Imaging.ColorMatrix.New( ;
   1 ,  0,  0,  0,  0, ;
   0 ,  1,  0,  0,  0, ;
   0 ,  0,  2,  0,  0, ;
   0 ,  0,  0,  1,  0, ;
   0 ,  0,  0,  0,  1)
Matrix5 - Adds 0.20 to the red component of each pixel in the image

loClrMatrix = .Imaging.ColorMatrix.New( ;
   1 ,  0,  0,  0,  0, ;
   0 ,  1,  0,  0,  0, ;
   0 ,  0,  1,  0,  0, ;
   0 ,  0,  0,  1,  0, ;
   .20 ,0,  0,  0,  1)
There are many other cool and impressive things that can be done using color matrices. In the next part of this article, I’ll try to show more things using more color matrices and other Image Attributes methods.

More about GDIPlus-X

In the source code of the new GDIPlus-X classes, you'll also find many other examples.

Go to the samples folder, and start running the file DEMO.PRG, and you'll have a good idea of the power of the class.

There you'll find 3 other examples that I've prepared related to this article, "ColorMatricesBasic.scx", "ColorMatricesAdvanced.scx" and "GammaThreshold.scx".

Most of the information below was extracted from some messages from Bo Durban and Craig Boyd in the "Sedna-X GotDotNet", "CodePlex" and some private messages.

One of the most interesting features of this library is the Image Canvas object, which is used in most of the samples that ship with the library. This class is extremely powerful for rendering graphics on a VFP form, making it SUPER easy for developers to create custom, graphics intensive controls, without having to worry about Paint events and off screen bitmaps. You only have to handle updates based on user interaction.

Almost all of the samples use the Image Canvas class. Take a look at the samples to see how easy it is to use. It can be as simple as adding your drawing commands to the BeforeDraw( ) method and calling the Draw() method, whenever you require an update.

At the core, this class gives you the ability to direct draw to a VFP Image control. The benefits of using this class over drawing directly to the form's HWnd include:

  • You draw once and VFP takes care of the Paint updates
  • Using this control allows you to respect z-order with other controls on the form
  • You can utilize the built in events, such as the Mouse and Click events
  • Allows you to subclass drawing commands
  • Allows you to encapsulate different drawing functionality
  • Allows you to have multiple drawing canvases on a single form
GdiPlus-X project is still in ALPHA version. If you are interested to help in the project, coding, testing making suggestions, in Codeplex you will find the Discussions and Issue Tracker sections. There's still a lot to be tested, as Bo said: "We welcome any feedback. Please let us know if you find any bugs in the classes that need to be addressed. If you are interested in helping with the development of this library, documentation or being an official tester, please contact the project manager Bo Durban at gdiplusx@moxiedata.com."

The reference manual for these libraries is still under development. To help you get started, in the "readme.htm" file you'll find a trick using intellisense to display a list of classes and members with their descriptions while writing code. Bo also included a preliminary reference document that list the classes included with the library and their current status. It also includes a brief description of each class with links to the equivalent .NET class in the MSDN library.

If you find any difficulty to work with the classes, or want to see GDI+ doing some specific task, feel free to ask at the codeplex message boards.

Cesar Chalom
Cesar is an independent software developer. He is an active member of brazilian Visual FoxPro groups.
More articles from this author
Cesar Chalom, June 1, 2006
Almost any application makes use of charts to display information to their users. Graphs are used for many different reasons, and can be found all over. We see them in the newspapers, magazines, and on television because they help us to communicate information. One of the commonest types of charts i...
Cesar Chalom, May 1, 2006
Certain Image types permit to save multiple images into a single file. It is possible to save several images to a single TIFF (Tagged Image File Format) file. GIF Image files also permit multi-frames, creating animated images, but is not totally supported by GDI+ version 1.
Cesar Chalom, April 1, 2006
Did you know that all the JPEGs from your digital camera contain a lot of extra information? We can easily retrieve some interesting information such as: Title, Equipment Make, Camera Model, Shutter Speed, Lens Aperture, Flash Mode, Date of Picture, and much more! These metadata "tags" are stored in...
Cesar Chalom, October 1, 2006
In the first part of this article, Cesar showed how we can apply some interesting effects to images with the use of color matrices and the new GdiPlus-X library. In this article, Cesar continues by showing more options to make use of some interesting applications of color matrices. This articles in...
Cesar Chalom, September 1, 2006
As a continuation of an article published in the Universal Thread Magazine May 2006 issue, entitled "Multiframe Images with GDI+", Cesar Chalom describes in this article how to manipulate TIFF images using the new GDIPlus-X library from VFP-X project. This article also shows how to create TIFFs usin...