Ask the Delphi Pro 10-Minute Solution

Creating A Skin Component
By Brendan Delumpa

Awhile back, I wrote an article about creating weirdly shaped forms that used WinAPI regions. They could create virtually any shape of form you wanted—rounded rectangles, ellipses, stars, or a combination of other shapes using the CombineRgn call. By recent standards this method was pretty low grade, but at the time, it was considered fairly fancy. Today, many programs utilize "skins" to change the appearance and shape of forms(think WinAmp). When I first saw WinAmp, I thought to myself, "They must be using regions to fit the form borders around a bitmap." But exactly how that was done was way beyond me. Well, thanks to Madshi over at the Experts Exchange (http://www.experts-exchange.com), I learned the mechanics of fitting a form's borders around a bitmap, and the solution is pretty slick! Below is code for a TImage descendant that you can drop on a form, and at runtime the form will become "transparent" except where the portions of the bitmap are shown. Take a look:

unit SkinImage;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, 
  ExtCtrls;

type
  TSkinImage = class(TImage)
  private

  protected
    { Protected declarations }
    function BitmapToRegion(bmp: TBitmap) : dword;
    procedure OwnerShow(Sender : TObject);
  public
    constructor Create(AOwner : TComponent); override;
  published
    { Published declarations }
  end;

procedure Register;

var
  Ready : Boolean;

implementation

procedure Register;
begin
  RegisterComponents('BD', [TSkinImage]);
end;

{ TSkinImage }

constructor TSkinImage.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  if NOT (csDesigning in ComponentState) then
    with TForm(AOwner) do
    begin
      BorderStyle := bsNone;
      Self.Top := 0;
      Self.Left := 0;
      OnShow := OwnerShow;
    end;
end;

function TSkinImage.BitmapToRegion(bmp: TBitmap) : dword;
var ix,iy : integer;    // loop variables
    tc    : TColor;     // transparentColor
    b1    : boolean;    // am looking through "real" 
pixels (no transparent pixels)
    c1    : cardinal;   // region helper variable
    i1    : integer;    // first position of real pixel
begin
  Result := 0;
  i1 := 0;
  // memory transparent color
  tc := bmp.transparentColor and $FFFFFF;
  with bmp.canvas do
    // scan through all lines
    for iy := 0 to bmp.height - 1 do
    begin
      b1 := False;
      // scan through all pixels in this line
      for ix:=0 to bmp.Width - 1 do
        // did we find the first/last real pixel in a row
        if (pixels[ix, iy] and $FFFFFF <> tc) <> b1 then begin
          // yes, and it was the last pixel,
          //so we can add a line style region...
          if b1 then begin
            c1:=CreateRectRgn(i1,iy,ix,iy+1);
            if result<>0 then
              begin
                // it's not the first region
                CombineRgn(Result, Result, c1, RGN_OR);
                DeleteObject(c1);
                // it's the first region
              end
            else
              Result := c1;
          end else i1 := ix;
          // change mode, looking for the first or last real pixel?
          b1:=not b1;
        end;
      // was the last pixel in this row a real pixel?
      if b1 then begin
        c1:=CreateRectRgn(i1, iy, bmp.width-1, iy+1);
        if (Result <> 0) then
          begin
            CombineRgn(Result, Result, c1, RGN_OR);
            DeleteObject(c1);
          end
        else
          Result := c1;
      end;
    end;
end;

procedure TSkinImage.OwnerShow(Sender: TObject);
var
  Region : HRGN;
begin
  if NOT Ready then
  begin
    Ready := True;
    Region := BitmapToRegion(Picture.Bitmap);
    SetWindowRgn(TForm(Owner).Handle, Region, True);
    DeleteObject(Region);
  end;
end;

initialization
  Ready := False;
end.
Notice that the workhorse of the component is the BitmapToRegion method. This method was developed by Madshi and kindly lent to me. In his own words, this is what the method does:

"Let's say the first line of our bitmap looks like this:

000XXXXX00XXXXX000000XXXX000 
0 -> transparent pixel;   X -> colored pixel 
Now my function goes through this line and creates a window region for each row of pixels. In the example, we would get 3 regions (4-8, 11-15, 22-25). Then I OR all the regions, and get a region for the complete pixel line. I do the same for all the other lines in the bitmap, and OR all those regions, and finally I end up with quite a complicated region that is something like the 'mask' of the bitmap. Now we have that region, the only thing left to do is to install it by calling SetWindowRgn."

Very cool. Try it out!
 
Other 10-Minute Solutions
 Trapping Messages Sent to an Application
 Getting the Number of Records From a Fixed-Length ASCII File
 Performing Incremental Searches with a TListbox
 Resizing the Drop-down List of a TComboBo
 Disabling the System Keys from Your Application
 Making A Secondary Form Independent of the Main Form
 Creating a System Tray Application
 Opening and Closing a CD Tray
 Hiding an Application from Windows
 Make a List Box Track the Mouse
 Running a Program at Startup
 Creating A Skin Component
 Dynamically Load Components From Packages at Run Time
 Obtain All Values from Multivalue Input Fields in WebBroker Applications
 Migrate Your BDE Applications to Linux with dbExpress
 Create a Multiline Button Component
 Update and Maintain dbExpress's Unidirectional, Read-Only Datasets
 Use ActionBands to Enable End Users to Customize Your Delphi Applications
 Produce HTML Reporting Output with WebBroker Components
  Convert XML Documents into Different Formats with the XSL Template Language


Ask the Delphi Pro | Who is the Pro? | Usage Policies | Ask a Question | Search | Feedback


Sponsored Links


Advertising Info  |   Member Services  |   Contact Us  |   Help  |   Feedback  |   Site Map
Jupiterweb networks

internet.comearthweb.comDevx.comClickZ

Search Jupiterweb:

Jupitermedia Corporation has four divisions:
JupiterWeb, JupiterResearch, JupiterEvents, and JupiterImages

Copyright 2004 Jupitermedia Corporation All Rights Reserved.
Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

Jupitermedia Corporate Info | Newsletters | Tech Jobs | E-mail Offers