Delphi 2009 GDI+ Library
This library enables GDI+ functionality for Delphi 2009 and later. It differs from other Delphi GDI+ libraries in the following ways:
- It is modeled more after the .NET System.Drawing namespace instead of the C++ GDI+ classes. As a result, this library is a bit more high level and easier to use.
- It uses object interfaces for automatic memory management and ease of use. You don't have to keep track of your graphics objects anymore.
- It uses exceptions instead of error codes to handle errors the Delphi way.
- It comes with sample applications that demonstrate the usage of GDI+ through examples from the Windows Platform SDK.
- It supports the GDI+ version 1.1 extensions that were added with Windows Vista and certain Office versions.
- Optionally provides class helpers for interoperability with Delphi's TBitmap and TCanvas.
Note that this GDI+ library only works with Delphi 2009 or later. This is because the library uses some new features of Delphi 2009 such as generics, and assumes that all string functions use Unicode strings.
The Delphi GDI+ library is completely free and can be downloaded here (1.5 MB). Please read the included License.txt file for license information.
|This library is also available on SourceForge and Embarcadero CodeCentral.|
Here are some more details about the differences with other GDI+ libraries you may know.
The TGPColor record is used to represent colors in GDI+. Most other libraries will use a LongWord to store colors and provide functions like MakeColor to create color values based on Red, Green, Blue and Alpha components. The TGPColor record used in this library is just a wrapper around a LongWord and just as efficient. It provides more high-level ways to create colors though. For example, the following declarations all create a fully opaque black color:
var Color: TGPColor; begin Color := TGPColor.Black; // Use a predefined color constant Color.Initialize(0, 0, 0); // Provide R, G and B values. Alpha is set to 255 Color.Initialize(255, 0, 0, 0); // Provide A, R, G and B values Color.Initialize($FF000000); // If you know the LongWord value Color := TGPColor.Create(0, 0, 0); Color := TGPColor.Create(255, 0, 0, 0); Color := TGPColor.Create($FF000000); Color.A := 255; Color.R := 0; Color.G := 0; Color.B := 0; Color.Value := $FF000000; // Another way to set the LongWord value directly Color := $FF000000; // Even shorter, using an implicit typecast Color.ColorRef := clBlack; // Use an old style GDI color end;
Just use the versions you are most comfortable with, or that are most appropriate for a certain situation. For example, the TGPColor.Create versions are useful when used as parameters in function calls, like in Graphics.Clear(TGPColor.Create(200, 30, 50)).
Other Simple Types
Other simple data types, like points, sizes and rects are also implemented with records, and have similar ways of using them. For example, a TGPPoint record can be initialized as follows:
The TGPPoint record also overloads the '+' operator, so you can add points together (as in the last line of the previous code).
var Point, OtherPoint, Sum: TGPPoint; begin Point.Initialize(1, 2); OtherPoint.Initialize(Point); Point := TGPPoint.Create(1, 2); Point.X := 1; Point.Y := 2; Sum := Point + OtherPoint; end;
All GDI+ classes are implemented using object interfaces. This simplifies memory management and allows for less code that is easier to maintain. Compare the following ways to draw a filled and outlined ellipse. The version on the left uses regular classes and the version on the right uses object interfaces (this is the version uses in this library).
|Using object interfaces|
When using object interfaces, you don't free any objects yourself. This is handled automatically by Delphi as the objects go out of scope. This makes the code shorter and reduces the chance for memory leak bugs.
If you are new to object interfaces, then here are some pointers (as far as the GDI+ library is concerned):
- Always declare variables of the interface type (IGPGraphics) and not of the class type (TGPGraphics).
- You use the class type only for constructing the object (eg. Graphics := TGPGraphics.Create(...)) or when calling a class function (FontFamily := TGPFontFamily.GenericSerif).
- Don't free the objects yourself (you cannot even call Free on an object interface). There may be situations where you need to free an object before it goes out of scope. You can accomplish this by setting the object reference to nil (eg. Graphics := nil). This will free the object if there are no other references to it.
- Other than that, you can use the GDI+ objects like regular objects.
Almost all C++ GDI+ functions return a status code to indicate if the function succeeded or failed. You would have to check the result of each function call and take appropriate action. This library uses exceptions instead. This way, your error checking code doesn't interrupt the normal flow of your code, and you can respond to errors in a Delphi way. When a GDI+ function fails, an exception of type EGdipError is raised. This exception class has a Status property that you can check to handle specific types of errors.
Properties instead of Getters and Setters
The C++ GDI+ classes, and some other Delphi GDI+ libraries, use Get- and Set-methods to set attributes of a object (for example, TGPGraphics.GetSmoothingMode and TGPGraphics.SetSmoothingMode). In this library, these have been replaced with properties where appropriate (for example, IGPGraphics.SmoothingMode).
Retrieving Complex Data
Some GDI+ classes have functions to retrieve more complex data, such as lists. For example, a GraphicsPath has a function to retrieve the points on the path. In the C++ model, and some Delphi models, the typical way to do this involves multiple steps:
- Call a function to retrieve the size of the data, or the number of elements in the list (for example GraphicsPath.GetPointCount);
- Allocate some memory large enough to hold the requested data (continuing the previous example, you would allocate a buffer of Point records);
- Call another function the retrieve the actual data (for example, GraphicsPath.GetPathPoints);
- Use the returned data;
- Free the memory you allocated in step 2.
In this library, this has been reduced to the following steps:
- Access a property that returns the data (for example, IGPGraphicsPath.PathPoints);
- Use the returned data.
For example, the IGraphics.PathPoints property returns an object of type IPathPoints, which is a generic array that holds records of type TPointF. (FYI: IPathPoints is declared as IArray<TPointF>). Since IPathPoints is an object interface, you don't have to worry about releasing it.
All properties that return arrays of some sort support iterators, so you can use the for..in syntax:
var Path: IGPGraphicsPath; Point: TGPPointF; begin Path := TGPGraphicsPath.Create; Path.AddEllipse(10, 10, 100, 100); for Point in Path.PathPoints do DoSomething(Point.X, Point.Y); end;
A similar model is used for GDI+ classes that return binary data. Normally you would request the size, allocate the memory, request the data, use the data and free the memory. In this library, there is just a single function call which returns an IGPBuffer object. This IGPBuffer object has a Size property and a Data property that points to the actual data. Again, you don't need to do any cleanup yourself.
Image Encoders and Decoders
Saving an image in a certain format (such as JPEG or PNG) takes a couple of steps in the C++ version. You would have to enumerate all encoders that are installed on the system to find the encoder you are interested in. This enumeration would also take a couple of steps. For standard formats like BMP, GIF, JPEG, PNG and TIFF, this library allows you the specify the format directly without any encoder enumeration:
You can still enumerate encoders if you need to. This has been simplified too since the class function TGPImageCodecInfo.GetImageEncoders returns an array of all installed encoders:
var Bitmap: IGPBitmap; begin Bitmap := TGPBitmap.Create(200, 100); Bitmap.Save('Output.png', TGPImageFormat.Png); end;
var Bitmap: IGPBitmap; Codec: IGPImageCodecInfo; begin Bitmap := TGPBitmap.Create(200, 100); for Codec in TGPImageCodecInfo.GetImageEncoders do if SameText(Codec.MimeType, 'image/targa') then begin Bitmap.Save('Output.tga', Codec); Break; end; end;
The example above assumes you have a TARGA encoder installed on you system (which you probably won't).
The library comes with 2 sample applications that demonstrate the usage of GDI+. The GdiPlus10 application shows the functionality of GDI+ version 1.0, and the GdiPlus11 application shows the additional functionality of version 1.1.
The GdiPlus10 application shows Delphi versions of examples found in the Windows Platform SDK. You can lookup these examples in the Platform SDK under the path "Platform SDK > Graphics and Multimedia Services > GDI+ > Using GDI+". However, the GdiPlus10 sample application shows the same documentation (but tailored to this Delphi version).
Version 1.1 of GDI+ adds some extra functionality to the original GDI+ version, most notably the addition of bitmap effects. Unfortunately, GDI+ 1.1 is only available on Windows Vista and later, or on computers with a certain version of Office (like Office 2007). Furthermore, the 1.1 version is not redistributable, so you are not allowed to deploy it with your application. However, you are free to use it if it is installed on a computer.
For these reasons, the GDI+ 1.1 extensions are disabled by default. To enable the functionality, you need to do the following in you Delphi application:
- Declare the conditional define 'GDIP_0110' (Project Options | Delphi Compiler | Conditional defines). This way, you don't accidentally use GDI+ 1.1 functionality in an application that must run on older versions of Windows.
- Disable runtime themes (Project Options | Application, uncheck 'Enable runtime themes'). This will not actually disable runtime themes, because the 'GDIP_0110' define causes the inclusion of different manifest file that enables both runtime themes and GDI+ 1.1.
These steps are required because the GDI+ 1.1 functionality can only be enabled by adding a side-by-side execution entry to you application manifest file. It is not sufficient to add the GDI+ 1.1 DLL to you application directory. When 'GDIP_0110' is defined, a different manifest is compiled into your application (see the GdiPlus11.manifest file for the details). This manifest enables both runtime themes (by using the Common Controls version 6) and GDI+ 1.1 (by using the 1.1 version of the GDI+ DLL).
Note however, that once you use this conditional define, that your application will not run on computers that do not have GDI+ 1.1 installed.
The GdiPlus11 sample application shows the additional functionality of GDI+ 1.1:
When you include the unit GdiPlusHelpers in your uses clause, it will add methods to some Delphi classes for easy access to an IGPGraphics object from any TCanvas, TGraphicControl or TCustomControl object. For example, to create an IGPGraphics object for a TPaintBox, you can use the following code:
var Graphics: IGPGraphics; begin Graphics := MyPaintBox.ToGPGraphics; end;
Without the class helpers you would accomplish the same result using the following code:
var Graphics: IGPGraphics; begin Graphics := TGraphics.Create(MyPaintBox.Canvas.Handle); end;
In the same manner, Delphi's TBitmap will be extended with the methods ToGPBitmap and FromGPBitmap.
These class helpers are purely optional and only provided as examples of an alternative way to create some GDI+ objects.