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.

Download

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.

Thanks to François Piette for adding support for using this library from a DLL.

Other sources

SourceForge This library is also available on SourceForge.

Here are some more details about the differences with other GDI+ libraries you may know.

Using Colors

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:

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;
The TGPPoint record also overloads the '+' operator, so you can add points together (as in the last line of the previous code).

Object Interfaces

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 classes
var
  Graphics: TGPGraphics;
  Pen: TGPPen;
  Brush: TGPBrush;
begin
  Graphics := TGPGraphics.Create(Canvas.Handle);
  try
    Pen := TGPPen.Create(MakeColor(30, 30, 30));
    try
      Brush := TGPSolidBrush.Create(MakeColor(200, 200, 200));
      try
        Graphics.FillEllipse(Brush, 10, 10, 50, 50);
        Graphics.DrawEllipse(Pen, 10, 10, 50, 50);
      finally
        Brush.Free;
      end;
    finally
      Pen.Free;
    end;
  finally
    Graphics.Free;
  end;
end;
Using object interfaces
var
  Graphics: IGPGraphics;
  Pen: IGPPen;
  Brush: IGPBrush;
begin
  Graphics := TGPGraphics.Create(Canvas.Handle);
  Pen := TGPPen.Create(TGPColor.Create(30, 30, 30));
  Brush := TGPSolidBrush.Create(TGPColor.Create(200, 200, 200));
  Graphics.FillEllipse(Brush, 10, 10, 50, 50);
  Graphics.DrawEllipse(Pen, 10, 10, 50, 50);
end;

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.

Exceptions instead of Error Codes

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:

  1. Call a function to retrieve the size of the data, or the number of elements in the list (for example GraphicsPath.GetPointCount);
  2. Allocate some memory large enough to hold the requested data (continuing the previous example, you would allocate a buffer of Point records);
  3. Call another function the retrieve the actual data (for example, GraphicsPath.GetPathPoints);
  4. Use the returned data;
  5. Free the memory you allocated in step 2.

In this library, this has been reduced to the following steps:

  1. Access a property that returns the data (for example, IGPGraphicsPath.PathPoints);
  2. 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:

var
  Bitmap: IGPBitmap;
begin
  Bitmap := TGPBitmap.Create(200, 100);
  Bitmap.Save('Output.png', TGPImageFormat.Png);
end;
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;
  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).

Sample Applications

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).

GDI+ 1.0 Demo

GDI+ 1.1 Extensions

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:

GDI+ 1.1 Extensions

Class Helpers

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.