Wednesday, November 19, 2008

New TPen properties

1. What is a Pen?

You know we are using pens when we draw a line or a shape border on a graphic canvas. A Pen defines how our lines will look. It can be thick or thin, dotted or solid, color or black. So lets review what properties a pen can have?

First of all pens can be cosmetic and geometric. Cosmetic pens are always 1 pixel wide, while geometric pens can have any width. But width of geometric pens are defined in world units and thus depends on transforms (scaling, rotation, shear, reflection) applied to canvas. Operations with cosmetic pens are faster.

The next pen property is pattern. Different libraries suggest different sets of default patterns (dot, dash, dash dot, dash dot dot, etc). Some of them don't contain any predefined pattern but allow to set a custom pattern. All libraries allow to set a solid pen pattern (usually default pattern) and some of then an empty pattern (invisible :) ).

The next usual pen property is color. Every library allows to set any custom pen color.

Geometric pens usually allow to set the style of end caps and the style of the joins between lines. The end cap can be: Round, Square and Flat. Look at the picture - they are placed in this order. When end caps are round - last points are drawn round, when end caps are square - last points are drawn square. When end caps are flat - last points are not drawn at all.
The join style can be: Round, Bevel and Miter. Look at the picture - they area placed in the same order.
Lazarus is using different libraries and each of them has a different pen implementation. For example Carbon does not supports cosmetic pens. And gtk does not support flat end caps for geometric pens (a similar style exists - GDK_CAP_NOT_LAST but it behaves as GDK_BUTT for geometric pens).

2. What was added and how it behaves

Before 0.9.27 pens were the same in Delphi and Lazarus. Pens were (and are) implemented through TPen class and had the following properties: Color, Style, Width (which cannot be less than 1). In order to create a widgetset object, TPen used a call to the CreatePenIndirect winapi compatibility function.

Thus I looked at how winapi works with all those end caps, join styles, cosmetic/geomtric and found another function which can create all what I need - ExtCreatePen. So the first thing I've done was inclusion of ExtCreatePen into LCL. I tried different pens by assigning that function result to Canvas.Pen.Handle. ExtCreatePen allows to choose whether your pen be cosmetic or geomtric, set a predefined line style or use custom dashes, set a solid color or even any brush to draw a line, set end cap style and join style. Don't expect full winapi compatibility from LCL, if you try to use this function directly. Only solid colors are supported - thus you need to pass BS_SOLID for the brush style and hatch = 0.

Another winapi compatibility function I touched was GetObject. It returns a description for the given GDI object. I've extended it to return correct TLogPen if a pen was created by CreatePenIndirect and TExtLogPen if a pen was created by ExtCreatePen.

Next I touched the TPen class itself. I've added TPenEndCap, TPenJoinStlye enumerations and TPen.EndCap, TPen.JoinStyle properties. Also I've added TPen.Cosmetic property since TPen.Width cannot be less than 1 (in other case I would use Pen.Width = 0 as cosmetic). So if you set TPen.Width = 1 and TPen.Cosmetic = True - you'll get your cosmetic pen (this is set by default for newly created pens). In all other cases pens are geometric. Pen is always geometric if Pen.Width > 1 even if Pen.Cosmetic = True. I've also added TPen.SetPattern method to set user defined patterns. If Pen.Style = psPattern lines will be drawn with dashes defined in the pattern array (first number means dash length, second space length and so forth).

3. Limitations

As I wrote before, Carbon does not support cosmetic pens and Gtk has its own point of view about Flat end caps :) But is this all you need to keep in mind?

When I wrote my test application and launched it on Mac OS X I've found that anti aliasing is not always a good friend :) My dashes were invisible and the pen looked almost solid for all styles when its width was equal to one. To eliminate this defects I've added TCanvas.Antialiasing property and now we can control it (only on Carbon and Qt though).

4. Conclusion

Almost all pen capabilities you can find in different libraries are now present in the LCL TPen class. If you missed ExtCreatePen or GetObject winapi functions - you can use them now too. If you don't understand something or just need more info then read msdn (ExtCreatePen), Qt help (QPen class reference) and Gtk reference manual (Graphics Contexts). If you want to look at TPen extensions yourself then launch our "Pen and Brush" example (I was inspired by a Qt Basic Drawing example when wrote it).

8 comments:

Unknown said...

Paul,
We are looking for help connecting to a MS SQL Server database from Lazarus running on Linux (using Zeos). Do you know anyone who could help us?
Thank you, Andy

Felipe Monteiro de Carvalho said...

Better ask this in the mailling list.

Vladimir Srednikh said...

By the way:
invisible pen can be helpful, when it need hide it for a while.
And then show it up without re-creating that same object

Anonymous said...

[quote]And gtk does not support flat end caps for geometric pens (a similar style exists - GDK_CAP_NOT_LAST but it behaves as GDK_BUTT for geometric pens).[/quote]

The sad thing is that GDK uses cairo or XLib directly. Not sue which or when. Anyway XLib supports flat end caps just fine and a few other types (I use it in fpGUI), so clearly it's missing feature (or bug) in GDK and should be reported as such.

Dariusz G. Jagielski said...

And when will be possible installing components without recompiling Lazarus?

Unknown said...

Rave, when FPC will support dynamic packages.

Jack said...

Thanks PAUL !! It is very well written article. While reading, I observed how briefly you explained everything either we talked about Pen's type (cosmetic and geometric) or pattern/color. All described fantastically.

forum widget

Unknown said...

I am very happy to be here because this is a very good site that provides lots of information about the topics covered in depth. I'm glad to see that people are actually writing about this issue in such a smart way, showing us all different sides to it. Please keep it up. I cant wait to read whats next.imprinted pens