Skip to content

Files

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Delphi 2009 GDI+ Library</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style>
body {
  background-color: #FFFFFF;
  color: #000000;
  font-family: 'Open Sans', tahoma, verdana, arial, helvetica, sans-serif;
  font-size: 11pt;
  scrollbar-base-color: #6633cc;
  scrollbar-arrow-color: #ffffff }
table {
  font-size: 10pt}
:link {
  text-decoration: underline;
  color: #9966ff }
:visited {
  text-decoration: underline;
  color: #9966ff }
:hover {
  text-decoration: none;
  background-color: #9966ff;
  color: #ffffff }
:active {
  text-decoration: none;
  background-color: #9966ff;
  color: #ffffff }
h1, h2, h3, h4, h5, h6 {
  color: #993300 }
.Tab {
  font-size: 13px }
.Tab A:link {
  text-decoration: none;
  color: #330099 }
.Tab A:visited {
  text-decoration: none;
  color: #330099 }
.Tab A:hover {
  text-decoration: none;
  background-color: #9966ff;
  color: #ffffff }
.Tab A:active {
  text-decoration: none;
  background-color: #ffffff;
  color: #330099 }
.FootNote {
	font-size: 8pt;
}
.Caption {
	font-size: 8pt;
	font-weight: bold;
}
.Formula {
	font-family: "Times New Roman", Times, serif;
	font-size: 10pt;
}
.Code {
	font-family: "Fira Mono", "Courier New", Courier, mono;
	background: #fef2b1;
	padding-left: 2px; padding-right: 2px;
	color: #000000;
}
.pas1-assembler { color: #800080; }
.pas1-character { color: #0000FF; }
.pas1-comment { color: #008000; font-style: italic; }
.pas1-float { color: #0000FF; }
.pas1-hexadecimal { color: #0000FF; }
.pas1-identifier { color: #000000; }
.pas1-number { color: #0000FF; }
.pas1-preprocessor { color: #008000; font-style: italic; }
.pas1-reservedword { color: #000080; font-weight: bold; }
.pas1-space { color: #000000; }
.pas1-string { color: #0000FF; }
.pas1-symbol { color: #000000; }
</style>
</head>

<body>
<h2>Delphi 2009 GDI+ Library</h2>
<p>This library enables GDI+ functionality for Delphi 2009 and later. It differs from other Delphi GDI+ libraries in the following ways:</p>
<ul>
  <li>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 <a href="#HighLevel">high level</a> and easier to use.</li>
  <li>It uses <a href="#ObjectInterfaces">object interfaces</a> for automatic memory management and ease of use. You don't have to keep track of your graphics objects anymore.</li>
  <li>It uses <a href="#Exceptions">exceptions</a> instead of error codes to handle errors the Delphi way.</li>
  <li>It comes with <a href="#Samples">sample applications</a> that demonstrate the usage of GDI+ through examples from the Windows Platform SDK.</li>
  <li>It supports the <a href="#Extensions">GDI+ version 1.1 extensions</a> that were added with Windows Vista and certain Office versions.</li>
  <li>Optionally provides <a href="#ClassHelpers">class helpers</a> for interoperability with Delphi's TBitmap and TCanvas.</li>
</ul>
<p>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.</p>
<p>Please read the <a href="License.txt">License.txt</a> file for license information.</p>
<p>Here are some more details about the differences with other GDI+ libraries you may know.</p>
<h2><a name="HighLevel"></a>Using Colors</h2>
<p>The <span class="Code">TGPColor</span> 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 <span class="Code">TGPColor</span> 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:</p>
<pre>
<code><span style="font: 10pt Fira Mono;"><span class="pas1-reservedword">var
</span><span class="pas1-space">  Color: TGPColor;
</span><span class="pas1-reservedword">begin
</span><span class="pas1-space">  Color := TGPColor.Black;           </span><span class="pas1-comment">// Use a predefined color constant
</span><span class="pas1-space">  Color.Initialize(</span><span class="pas1-number">0</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">);         </span><span class="pas1-comment">// Provide R, G and B values. Alpha is set to 255
</span><span class="pas1-space">  Color.Initialize(</span><span class="pas1-number">255</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">);    </span><span class="pas1-comment">// Provide A, R, G and B values
</span><span class="pas1-space">  Color.Initialize(</span><span class="pas1-hexadecimal">$FF000000</span><span class="pas1-symbol">);       </span><span class="pas1-comment">// If you know the LongWord value
</span><span class="pas1-space">  Color := TGPColor.Create(</span><span class="pas1-number">0</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">);
  Color := TGPColor.Create(</span><span class="pas1-number">255</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">, </span><span class="pas1-number">0</span><span class="pas1-symbol">);
  Color := TGPColor.Create(</span><span class="pas1-hexadecimal">$FF000000</span><span class="pas1-symbol">);
  Color.A := </span><span class="pas1-number">255</span><span class="pas1-symbol">; Color.R := </span><span class="pas1-number">0</span><span class="pas1-symbol">; Color.G := </span><span class="pas1-number">0</span><span class="pas1-symbol">; Color.B := </span><span class="pas1-number">0</span><span class="pas1-symbol">;
  Color.Value := </span><span class="pas1-hexadecimal">$FF000000</span><span class="pas1-symbol">;          </span><span class="pas1-comment">// Another way to set the LongWord value directly
</span><span class="pas1-space">  Color := </span><span class="pas1-hexadecimal">$FF000000</span><span class="pas1-symbol">;                </span><span class="pas1-comment">// Even shorter, using an implicit typecast
</span><span class="pas1-space">  Color.ColorRef := clBlack;         </span><span class="pas1-comment">// Use an old style GDI color
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;</span></span>
</code></pre><p>Just use the versions you are most comfortable with, or that are most appropriate for a certain situation. For example, the <span class="Code">TGPColor.Create</span> versions are useful when used as parameters in function calls, like in <span class="Code">Graphics.Clear(TGPColor.Create(200, 30, 50))</span>.</p>
<h2>Other Simple Types</h2>
<p>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:</p>
<pre>
<code><span style="font: 10pt Fira Mono;"><span class="pas1-reservedword">var
</span><span class="pas1-space">  Point, OtherPoint, Sum: TGPPoint;
</span><span class="pas1-reservedword">begin
</span><span class="pas1-space">  Point.Initialize(</span><span class="pas1-number">1</span><span class="pas1-symbol">, </span><span class="pas1-number">2</span><span class="pas1-symbol">);
  OtherPoint.Initialize(Point);
  Point := TGPPoint.Create(</span><span class="pas1-number">1</span><span class="pas1-symbol">, </span><span class="pas1-number">2</span><span class="pas1-symbol">);
  Point.X := </span><span class="pas1-number">1</span><span class="pas1-symbol">; Point.Y := </span><span class="pas1-number">2</span><span class="pas1-symbol">;
  Sum := Point + OtherPoint;
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;</span></span></code></pre>
The TGPPoint record also overloads the '+' operator, so you can add points together (as in the last line of the previous code).
<h2><a name="ObjectInterfaces"></a>Object Interfaces</h2>
<p>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).</p>
<table width="100%" border="0">
  <tr>
    <td bgcolor="ccccff"><strong>Using classes</strong></td>
    <td bgcolor="ccccff"><strong>Using object interfaces</strong></td>
  </tr>
  <tr>
    <td valign="top" bgcolor="ddddff"><pre>
<code><span style="font: 10pt Fira Mono;"><span class="pas1-reservedword">var
</span><span class="pas1-space">  Graphics: TGPGraphics;
  Pen: TGPPen;
  Brush: TGPBrush;
</span><span class="pas1-reservedword">begin
</span><span class="pas1-space">  Graphics := TGPGraphics.Create(Canvas.Handle);
  </span><span class="pas1-reservedword">try
</span><span class="pas1-space">    Pen := TGPPen.Create(MakeColor(</span><span class="pas1-number">30</span><span class="pas1-symbol">, </span><span class="pas1-number">30</span><span class="pas1-symbol">, </span><span class="pas1-number">30</span><span class="pas1-symbol">));
    </span><span class="pas1-reservedword">try
</span><span class="pas1-space">      Brush := TGPSolidBrush.Create(MakeColor(</span><span class="pas1-number">200</span><span class="pas1-symbol">, </span><span class="pas1-number">200</span><span class="pas1-symbol">, </span><span class="pas1-number">200</span><span class="pas1-symbol">));
      </span><span class="pas1-reservedword">try
</span><span class="pas1-space">        Graphics.FillEllipse(Brush, </span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">50</span><span class="pas1-symbol">, </span><span class="pas1-number">50</span><span class="pas1-symbol">);
        Graphics.DrawEllipse(Pen, </span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">50</span><span class="pas1-symbol">, </span><span class="pas1-number">50</span><span class="pas1-symbol">);
      </span><span class="pas1-reservedword">finally
</span><span class="pas1-space">        Brush.Free;
      </span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;
    </span><span class="pas1-reservedword">finally
</span><span class="pas1-space">      Pen.Free;
    </span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;
  </span><span class="pas1-reservedword">finally
</span><span class="pas1-space">    Graphics.Free;
  </span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;

</span></span>
</code></pre>
</td>
    <td valign="top" bgcolor="ddddff"><pre>
<code><span style="font: 10pt Fira Mono;"><span class="pas1-reservedword">var
</span><span class="pas1-space">  Graphics: IGPGraphics;
  Pen: IGPPen;
  Brush: IGPBrush;
</span><span class="pas1-reservedword">begin
</span><span class="pas1-space">  Graphics := TGPGraphics.Create(Canvas.Handle);
  Pen := TGPPen.Create(TGPColor.Create(</span><span class="pas1-number">30</span><span class="pas1-symbol">, </span><span class="pas1-number">30</span><span class="pas1-symbol">, </span><span class="pas1-number">30</span><span class="pas1-symbol">));
  Brush := TGPSolidBrush.Create(TGPColor.Create(</span><span class="pas1-number">200</span><span class="pas1-symbol">, </span><span class="pas1-number">200</span><span class="pas1-symbol">, </span><span class="pas1-number">200</span><span class="pas1-symbol">));
  Graphics.FillEllipse(Brush, </span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">50</span><span class="pas1-symbol">, </span><span class="pas1-number">50</span><span class="pas1-symbol">);
  Graphics.DrawEllipse(Pen, </span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">50</span><span class="pas1-symbol">, </span><span class="pas1-number">50</span><span class="pas1-symbol">);
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;

</span></span>
</code></pre>
</td>
  </tr>
</table>
<p>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.</p>
<p>If you are new to object interfaces, then here are some pointers (as far as the GDI+ library is concerned):</p>
<ul>
  <li>Always declare variables of the interface type (IGPGraphics) and <strong>not</strong> of the class type (TGPGraphics).</li>
  <li>You use the class type only for constructing the object (eg. <span class="Code">Graphics := TGPGraphics.Create(...)</span>) or when calling a class function (<span class="Code">FontFamily := TGPFontFamily.GenericSerif</span>).</li>
  <li>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. <span class="Code">Graphics := nil</span>). This will free the object if there are no other references to it.</li>
  <li>Other than that, you can use the GDI+ objects like regular objects.</li>
</ul>
<h2><a name="Exceptions"></a>Exceptions instead of Error Codes</h2>
<p>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.</p>
<h2>Properties instead of Getters and Setters</h2>
<p>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).</p>
<h2>Retrieving Complex Data</h2>
<p>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:</p>
<ol>
  <li>Call a function to retrieve the size of the data, or the number of elements in the list (for example GraphicsPath.GetPointCount);</li>
  <li>Allocate some memory large enough to hold the requested data (continuing the previous example, you would allocate a buffer of Point records);</li>
  <li>Call another function the retrieve the actual data (for example, GraphicsPath.GetPathPoints);</li>
  <li>Use the returned data;</li>
  <li>Free the memory you allocated in step 2.</li>
</ol>
<p>In this library, this has been reduced to the following steps:</p>
<ol>
  <li>Access a property that returns the data (for example, IGPGraphicsPath.PathPoints);</li>
  <li>Use the returned data.</li>
</ol>
<p>For example, the IGraphics.PathPoints property returns an object of type IPathPoints, which is a generic array that holds records of type TPointF. (FYI: <span class="Code">IPathPoints</span> is declared as <span class="Code">IArray&lt;TPointF&gt;</span>). Since IPathPoints is an object interface, you don't have to worry about releasing it.</p>
<p>All properties that return arrays of some sort support iterators, so you can use the <span class="Code">for..in</span> syntax:</p>
<pre>
<code><span style="font: 10pt Fira Mono;"><span class="pas1-reservedword">var
</span><span class="pas1-space">  Path: IGPGraphicsPath;
  Point: TGPPointF;
</span><span class="pas1-reservedword">begin
</span><span class="pas1-space">  Path := TGPGraphicsPath.Create;
  Path.AddEllipse(</span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">10</span><span class="pas1-symbol">, </span><span class="pas1-number">100</span><span class="pas1-symbol">, </span><span class="pas1-number">100</span><span class="pas1-symbol">);
  </span><span class="pas1-reservedword">for</span><span class="pas1-space"> Point </span><span class="pas1-reservedword">in</span><span class="pas1-space"> Path.PathPoints </span><span class="pas1-reservedword">do
</span><span class="pas1-space">    DoSomething(Point.X, Point.Y);
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;</span></span></code></pre>
<p>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.</p>
<h2>Image Encoders and Decoders</h2>
<p>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:</p>
<pre>
<code><span style="font: 10pt Fira Mono;"><span class="pas1-reservedword">var
</span><span class="pas1-space">  Bitmap: IGPBitmap;
</span><span class="pas1-reservedword">begin
</span><span class="pas1-space">  Bitmap := TGPBitmap.Create(</span><span class="pas1-number">200</span><span class="pas1-symbol">, </span><span class="pas1-number">100</span><span class="pas1-symbol">);
  Bitmap.Save(</span><span class="pas1-string">'Output.png'</span><span class="pas1-symbol">, TGPImageFormat.Png);
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;</span></span></code></pre>
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:
<pre>
<code><span style="font: 10pt Fira Mono;"><span class="pas1-reservedword">var
</span><span class="pas1-space">  Bitmap: IGPBitmap;
  Codec: IGPImageCodecInfo;
</span><span class="pas1-reservedword">begin
</span><span class="pas1-space">  Bitmap := TGPBitmap.Create(</span><span class="pas1-number">200</span><span class="pas1-symbol">, </span><span class="pas1-number">100</span><span class="pas1-symbol">);
  </span><span class="pas1-reservedword">for</span><span class="pas1-space"> Codec </span><span class="pas1-reservedword">in</span><span class="pas1-space"> TGPImageCodecInfo.GetImageEncoders </span><span class="pas1-reservedword">do
</span><span class="pas1-space">    </span><span class="pas1-reservedword">if</span><span class="pas1-space"> SameText(Codec.MimeType, </span><span class="pas1-string">'image/targa'</span><span class="pas1-symbol">) </span><span class="pas1-reservedword">then
</span><span class="pas1-space">    </span><span class="pas1-reservedword">begin
</span><span class="pas1-space">      Bitmap.Save(</span><span class="pas1-string">'Output.tga'</span><span class="pas1-symbol">, Codec);
      Break;
    </span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;</span></span></code></pre>
<p>The example above assumes you have a TARGA encoder installed on you system (which you probably won't).</p>
<h2><a name="Samples"></a>Sample Applications</h2>
<p>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.</p>
<p>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 &quot;Platform SDK &gt; Graphics and Multimedia Services &gt; GDI+ &gt; Using GDI+&quot;. However, the GdiPlus10 sample application shows the same documentation (but tailored to this Delphi version).</p>
<p><img src="GdiPlus10.gif" alt="GDI+ 1.0 Demo" width="873" height="759"></p>
<h2><a name="Extensions"></a>GDI+ 1.1 Extensions</h2>
<p>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.</p>
<p>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:</p>
<ul>
  <li>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.</li>
  <li>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.</li>
</ul>
<p>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).</p>
<p>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.</p>
<p>The GdiPlus11 sample application shows the additional functionality of GDI+ 1.1:</p>
<p><img src="GdiPlus11.gif" alt="GDI+ 1.1 Extensions" width="1169" height="698"></p>
<h2><a name="ClassHelpers"></a>Class Helpers</h2>
<p>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:</p>
<pre>
<code><span style="font: 10pt Fira Mono;"><span class="pas1-reservedword">var
</span><span class="pas1-space">  Graphics: IGPGraphics;
</span><span class="pas1-reservedword">begin
</span><span class="pas1-space">  Graphics := MyPaintBox.ToGPGraphics;
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;</span></span></code></pre>
<p>Without the class helpers you would accomplish the same result using the following code:</p>
<pre>
<code><span style="font: 10pt Fira Mono;"><span class="pas1-reservedword">var
</span><span class="pas1-space">  Graphics: IGPGraphics;
</span><span class="pas1-reservedword">begin
</span><span class="pas1-space">  Graphics := TGraphics.Create(MyPaintBox.Canvas.Handle);
</span><span class="pas1-reservedword">end</span><span class="pas1-symbol">;</span></span></code></pre>
<p>In the same manner, Delphi's TBitmap will be extended with the methods ToGPBitmap and FromGPBitmap.</p>
<p>These class helpers are purely optional and only provided as examples of an alternative way to create some GDI+ objects.</p>
</body>
</html>