home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1997 April
/
Chip_1997-04_cd.bin
/
prezent
/
cb
/
data.z
/
GRAPHICS.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1997-01-16
|
138KB
|
5,003 lines
{*******************************************************}
{ }
{ Delphi Visual Component Library }
{ }
{ Copyright (c) 1995-1997 Borland International }
{ }
{*******************************************************}
unit Graphics; // $Revision: 1.21 $
{$P+,S-,W-,R-}
{$C PRELOAD}
interface
uses Windows, SysUtils, Classes;
{ Graphics Objects }
type
TColor = $80000000..$7FFFFFFF;
const
clScrollBar = TColor(COLOR_SCROLLBAR or $80000000);
clBackground = TColor(COLOR_BACKGROUND or $80000000);
clActiveCaption = TColor(COLOR_ACTIVECAPTION or $80000000);
clInactiveCaption = TColor(COLOR_INACTIVECAPTION or $80000000);
clMenu = TColor(COLOR_MENU or $80000000);
clWindow = TColor(COLOR_WINDOW or $80000000);
clWindowFrame = TColor(COLOR_WINDOWFRAME or $80000000);
clMenuText = TColor(COLOR_MENUTEXT or $80000000);
clWindowText = TColor(COLOR_WINDOWTEXT or $80000000);
clCaptionText = TColor(COLOR_CAPTIONTEXT or $80000000);
clActiveBorder = TColor(COLOR_ACTIVEBORDER or $80000000);
clInactiveBorder = TColor(COLOR_INACTIVEBORDER or $80000000);
clAppWorkSpace = TColor(COLOR_APPWORKSPACE or $80000000);
clHighlight = TColor(COLOR_HIGHLIGHT or $80000000);
clHighlightText = TColor(COLOR_HIGHLIGHTTEXT or $80000000);
clBtnFace = TColor(COLOR_BTNFACE or $80000000);
clBtnShadow = TColor(COLOR_BTNSHADOW or $80000000);
clGrayText = TColor(COLOR_GRAYTEXT or $80000000);
clBtnText = TColor(COLOR_BTNTEXT or $80000000);
clInactiveCaptionText = TColor(COLOR_INACTIVECAPTIONTEXT or $80000000);
clBtnHighlight = TColor(COLOR_BTNHIGHLIGHT or $80000000);
cl3DDkShadow = TColor(COLOR_3DDKSHADOW or $80000000);
cl3DLight = TColor(COLOR_3DLIGHT or $80000000);
clInfoText = TColor(COLOR_INFOTEXT or $80000000);
clInfoBk = TColor(COLOR_INFOBK or $80000000);
clBlack = TColor($000000);
clMaroon = TColor($000080);
clGreen = TColor($008000);
clOlive = TColor($008080);
clNavy = TColor($800000);
clPurple = TColor($800080);
clTeal = TColor($808000);
clGray = TColor($808080);
clSilver = TColor($C0C0C0);
clRed = TColor($0000FF);
clLime = TColor($00FF00);
clYellow = TColor($00FFFF);
clBlue = TColor($FF0000);
clFuchsia = TColor($FF00FF);
clAqua = TColor($FFFF00);
clLtGray = TColor($C0C0C0);
clDkGray = TColor($808080);
clWhite = TColor($FFFFFF);
clNone = TColor($1FFFFFFF);
clDefault = TColor($20000000);
const
cmBlackness = BLACKNESS;
cmDstInvert = DSTINVERT;
cmMergeCopy = MERGECOPY;
cmMergePaint = MERGEPAINT;
cmNotSrcCopy = NOTSRCCOPY;
cmNotSrcErase = NOTSRCERASE;
cmPatCopy = PATCOPY;
cmPatInvert = PATINVERT;
cmPatPaint = PATPAINT;
cmSrcAnd = SRCAND;
cmSrcCopy = SRCCOPY;
cmSrcErase = SRCERASE;
cmSrcInvert = SRCINVERT;
cmSrcPaint = SRCPAINT;
cmWhiteness = WHITENESS;
type
HMETAFILE = THandle;
{$nonamespace HMETAFILE}
HENHMETAFILE = THandle;
{$nonamespace HENHMETAFILE}
EInvalidGraphic = class(Exception);
EInvalidGraphicOperation = class(Exception);
TGraphic = class;
TBitmap = class;
TIcon = class;
TMetafile = class;
TResData = record
Handle: THandle;
end;
TFontStyle = (fsBold, fsItalic, fsUnderline, fsStrikeOut);
TFontStyles = set of TFontStyle;
TFontPitch = (fpDefault, fpVariable, fpFixed);
TFontName = string[LF_FACESIZE - 1];
TFontCharset = 0..255;
TFontData = record
Handle: HFont;
Height: Integer;
Pitch: TFontPitch;
Style: TFontStyles;
Charset: TFontCharset;
Name: TFontName;
end;
TDummyFontStyles = set of TFontStyle;
TDummyFontName = string[LF_FACESIZE - 1];
TDummyFontData = record
Handle: HFont;
Height: Integer;
Pitch: TFontPitch;
Style: TDummyFontStyles;
Charset: TFontCharset;
Name: TDummyFontName;
end;
TPenStyle = (psSolid, psDash, psDot, psDashDot, psDashDotDot, psClear,
psInsideFrame);
TPenMode = (pmBlack, pmWhite, pmNop, pmNot, pmCopy, pmNotCopy,
pmMergePenNot, pmMaskPenNot, pmMergeNotPen, pmMaskNotPen, pmMerge,
pmNotMerge, pmMask, pmNotMask, pmXor, pmNotXor);
TPenData = record
Handle: HPen;
Color: TColor;
Width: Integer;
Style: TPenStyle;
end;
TBrushStyle = (bsSolid, bsClear, bsHorizontal, bsVertical,
bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross);
TBrushData = record
Handle: HBrush;
Color: TColor;
Bitmap: TBitmap;
Style: TBrushStyle;
end;
PResource = ^TResource;
TResource = record
Next: PResource;
RefCount: Integer;
Handle: THandle;
HashCode: Word;
case Integer of
0: (Data: TResData);
1: (Font: TFontData);
2: (Pen: TPenData);
3: (Brush: TBrushData);
end;
TGraphicsObject = class(TPersistent)
private
FOnChange: TNotifyEvent;
FResource: PResource;
protected
procedure Changed; dynamic;
public
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
TFont = class(TGraphicsObject)
private
FColor: TColor;
FPixelsPerInch: Integer;
procedure GetData(var FontData: TFontData);
procedure SetData(const FontData: TFontData);
protected
function GetHandle: HFont;
function GetHeight: Integer;
function GetName: TFontName;
function GetPitch: TFontPitch;
function GetSize: Integer;
function GetStyle: TFontStyles;
function GetCharset: TFontCharset;
procedure SetColor(Value: TColor);
procedure SetHandle(Value: HFont);
procedure SetHeight(Value: Integer);
procedure SetName(const Value: TFontName);
procedure SetPitch(Value: TFontPitch);
procedure SetSize(Value: Integer);
procedure SetStyle(Value: TFontStyles);
procedure SetCharset(Value: TFontCharset);
public
constructor Create;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
property Handle: HFont read GetHandle write SetHandle;
property PixelsPerInch: Integer read FPixelsPerInch write FPixelsPerInch;
published
property Charset: TFontCharset read GetCharset write SetCharset;
property Color: TColor read FColor write SetColor;
property Height: Integer read GetHeight write SetHeight;
property Name: TFontName read GetName write SetName;
property Pitch: TFontPitch read GetPitch write SetPitch default fpDefault;
property Size: Integer read GetSize write SetSize stored False;
property Style: TFontStyles read GetStyle write SetStyle;
end;
TPen = class(TGraphicsObject)
private
FMode: TPenMode;
procedure GetData(var PenData: TPenData);
procedure SetData(const PenData: TPenData);
protected
function GetColor: TColor;
procedure SetColor(Value: TColor);
function GetHandle: HPen;
procedure SetHandle(Value: HPen);
procedure SetMode(Value: TPenMode);
function GetStyle: TPenStyle;
procedure SetStyle(Value: TPenStyle);
function GetWidth: Integer;
procedure SetWidth(Value: Integer);
public
constructor Create;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
property Handle: HPen read GetHandle write SetHandle;
published
property Color: TColor read GetColor write SetColor default clBlack;
property Mode: TPenMode read FMode write SetMode default pmCopy;
property Style: TPenStyle read GetStyle write SetStyle default psSolid;
property Width: Integer read GetWidth write SetWidth default 1;
end;
TBrush = class(TGraphicsObject)
private
procedure GetData(var BrushData: TBrushData);
procedure SetData(const BrushData: TBrushData);
protected
function GetBitmap: TBitmap;
procedure SetBitmap(Value: TBitmap);
function GetColor: TColor;
procedure SetColor(Value: TColor);
function GetHandle: HBrush;
procedure SetHandle(Value: HBrush);
function GetStyle: TBrushStyle;
procedure SetStyle(Value: TBrushStyle);
public
constructor Create;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
property Bitmap: TBitmap read GetBitmap write SetBitmap;
property Handle: HBrush read GetHandle write SetHandle;
published
property Color: TColor read GetColor write SetColor default clWhite;
property Style: TBrushStyle read GetStyle write SetStyle default bsSolid;
end;
TFillStyle = (fsSurface, fsBorder);
TFillMode = (fmAlternate, fmWinding);
TCopyMode = Longint;
TCanvasStates = (csHandleValid, csFontValid, csPenValid, csBrushValid);
TCanvasState = set of TCanvasStates;
TCanvas = class(TPersistent)
private
FHandle: HDC;
State: TCanvasState;
FFont: TFont;
FPen: TPen;
FBrush: TBrush;
FPenPos: TPoint;
FCopyMode: TCopyMode;
FOnChange: TNotifyEvent;
FOnChanging: TNotifyEvent;
procedure CreateBrush;
procedure CreateFont;
procedure CreatePen;
procedure BrushChanged(ABrush: TObject);
procedure DeselectHandles;
function GetClipRect: TRect;
function GetHandle: HDC;
function GetPenPos: TPoint;
function GetPixel(X, Y: Integer): TColor;
procedure FontChanged(AFont: TObject);
procedure PenChanged(APen: TObject);
procedure SetBrush(Value: TBrush);
procedure SetFont(Value: TFont);
procedure SetHandle(Value: HDC);
procedure SetPen(Value: TPen);
procedure SetPenPos(Value: TPoint);
procedure SetPixel(X, Y: Integer; Value: TColor);
protected
procedure Changed; virtual;
procedure Changing; virtual;
procedure CreateHandle; virtual;
procedure RequiredState(ReqState: TCanvasState);
public
constructor Create;
destructor Destroy; override;
procedure Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
procedure BrushCopy(const Dest: TRect; Bitmap: TBitmap;
const Source: TRect; Color: TColor);
procedure Chord(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
procedure CopyRect(const Dest: TRect; Canvas: TCanvas;
const Source: TRect);
procedure Draw(X, Y: Integer; Graphic: TGraphic);
procedure DrawFocusRect(const Rect: TRect);
procedure Ellipse(X1, Y1, X2, Y2: Integer);
procedure FillRect(const Rect: TRect);
procedure FloodFill(X, Y: Integer; Color: TColor; FillStyle: TFillStyle);
procedure FrameRect(const Rect: TRect);
procedure LineTo(X, Y: Integer);
procedure MoveTo(X, Y: Integer);
procedure Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
procedure Polygon(const Points: array of TPoint);
procedure Polyline(const Points: array of TPoint);
procedure Rectangle(X1, Y1, X2, Y2: Integer);
procedure Refresh;
procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
procedure StretchDraw(const Rect: TRect; Graphic: TGraphic);
function TextHeight(const Text: string): Integer;
procedure TextOut(X, Y: Integer; const Text: string);
procedure TextRect(Rect: TRect; X, Y: Integer; const Text: string);
function TextWidth(const Text: string): Integer;
property ClipRect: TRect read GetClipRect;
property Handle: HDC read GetHandle write SetHandle;
property PenPos: TPoint read GetPenPos write SetPenPos;
property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
published
property Brush: TBrush read FBrush write SetBrush;
property CopyMode: TCopyMode read FCopyMode write FCopyMode default cmSrcCopy;
property Font: TFont read FFont write SetFont;
property Pen: TPen read FPen write SetPen;
end;
{ The TGraphic class is a abstract base class for dealing with graphic images
such as metafile, bitmaps and icons; but is not limited to such.
LoadFromFile - Read the graphic from the file system. The old contents of
the graphic are lost. If the file is not of the right format, an
exception will be generated.
SaveToFile - Writes the graphic to disk in the file provided.
LoadFromStream - Like LoadFromFile except source is a stream (e.g.
TBlobStream).
SaveToStream - stream analogue of SaveToFile.
LoadFromClipboardFormat - Replaces the current image with the data
provided. If the TGraphic does not support that format it will generate
an exception.
SaveToClipboardFormats - Converts the image to a clipboard format. If the
image does not support being translated into a clipboard format it
will generate an exception.
Height - The native, unstretched, height of the graphic.
Width - The native, unstretched, width of the graphic.
OnChange - Called whenever the graphic changes }
TGraphic = class(TPersistent)
private
FOnChange: TNotifyEvent;
FModified: Boolean;
FReserved: Byte;
procedure SetModified(Value: Boolean);
protected
constructor Create; virtual;
procedure Changed(Sender: TObject);
procedure DefineProperties(Filer: TFiler); override;
procedure Draw(ACanvas: TCanvas; const Rect: TRect); virtual; abstract;
function Equals(Graphic: TGraphic): Boolean; virtual;
function GetEmpty: Boolean; virtual; abstract;
function GetHeight: Integer; virtual; abstract;
function GetWidth: Integer; virtual; abstract;
procedure ReadData(Stream: TStream); virtual;
procedure SetHeight(Value: Integer); virtual; abstract;
procedure SetWidth(Value: Integer); virtual; abstract;
procedure WriteData(Stream: TStream); virtual;
public
procedure LoadFromFile(const Filename: string); virtual;
procedure SaveToFile(const Filename: string); virtual;
procedure LoadFromStream(Stream: TStream); virtual; abstract;
procedure SaveToStream(Stream: TStream); virtual; abstract;
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE); virtual; abstract;
procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE); virtual; abstract;
property Empty: Boolean read GetEmpty;
property Height: Integer read GetHeight write SetHeight;
property Modified: Boolean read FModified write SetModified;
property Width: Integer read GetWidth write SetWidth;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
TGraphicClass = class of TGraphic;
{ TPicture }
{ TPicture is a TGraphic container. It is used in place of a TGraphic if the
graphic can be of any TGraphic class. LoadFromFile and SaveToFile are
polymorphic. For example, if the TPicture is holding an Icon, you can
LoadFromFile a bitmap file, where if the class was TIcon you could only read
.ICO files.
LoadFromFile - Reads a picture from disk. The TGraphic class created
determined by the file extension of the file. If the file extension is
not recognized an exception is generated.
SaveToFile - Writes the picture to disk.
LoadFromClipboardFormat - Reads the picture from the handle provided in
the given clipboard format. If the format is not supported, an
exception is generated.
SaveToClipboardFormats - Allocates a global handle and writes the picture
in its native clipboard format (CF_BITMAP for bitmaps, CF_METAFILE
for metafiles, etc.). Formats will contain the formats written.
Returns the number of clipboard items written to the array pointed to
by Formats and Datas or would be written if either Formats or Datas are
nil.
SupportsClipboardFormat - Returns true if the given clipboard format
is supported by LoadFromClipboardFormat.
Assign - Copys the contents of the given TPicture. Used most often in
the implementation of TPicture properties.
RegisterFileFormat - Register a new TGraphic class for use in
LoadFromFile.
RegisterClipboardFormat - Registers a new TGraphic class for use in
LoadFromClipboardFormat.
Height - The native, unstretched, height of the picture.
Width - The native, unstretched, width of the picture.
Graphic - The TGraphic object contained by the TPicture
Bitmap - Returns a bitmap. If the contents is not already a bitmap, the
contents are thrown away and a blank bitmap is returned.
Icon - Returns an icon. If the contents is not already an icon, the
contents are thrown away and a blank icon is returned.
Metafile - Returns a metafile. If the contents is not already a metafile,
the contents are thrown away and a blank metafile is returned. }
TPicture = class(TPersistent)
private
FGraphic: TGraphic;
FOnChange: TNotifyEvent;
procedure ForceType(GraphicType: TGraphicClass);
function GetBitmap: TBitmap;
function GetHeight: Integer;
function GetIcon: TIcon;
function GetMetafile: TMetafile;
function GetWidth: Integer;
procedure ReadData(Stream: TStream);
procedure SetBitmap(Value: TBitmap);
procedure SetGraphic(Value: TGraphic);
procedure SetIcon(Value: TIcon);
procedure SetMetafile(Value: TMetafile);
procedure WriteData(Stream: TStream);
protected
procedure AssignTo(Dest: TPersistent); override;
procedure Changed(Sender: TObject);
procedure DefineProperties(Filer: TFiler); override;
public
destructor Destroy; override;
procedure LoadFromFile(const Filename: string);
procedure SaveToFile(const Filename: string);
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE);
class function SupportsClipboardFormat(AFormat: Word): Boolean;
procedure Assign(Source: TPersistent); override;
class procedure RegisterFileFormat(const AExtension, ADescription: string;
AGraphicClass: TGraphicClass);
class procedure RegisterFileFormatRes(const AExtension: String;
ADescriptionResID: Integer; AGraphicClass: TGraphicClass);
class procedure RegisterClipboardFormat(AFormat: Word;
AGraphicClass: TGraphicClass);
property Bitmap: TBitmap read GetBitmap write SetBitmap;
property Graphic: TGraphic read FGraphic write SetGraphic;
property Height: Integer read GetHeight;
property Icon: TIcon read GetIcon write SetIcon;
property Metafile: TMetafile read GetMetafile write SetMetafile;
property Width: Integer read GetWidth;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
end;
{ TMetafile }
{ TMetafile is an encapsulation of the Win32 Enhanced metafile.
Handle - The metafile handle.
Enhanced - determines how the metafile will be stored on disk.
Enhanced = True (default) stores as EMF (Win32 Enhanced Metafile),
Enhanced = False stores as WMF (Windows 3.1 Metafile, with Aldus header).
The in-memory format is always EMF. WMF has very limited capabilities;
storing as WMF will lose information that would be retained by EMF.
This property is set to match the metafile type when loaded from a
stream or file. This maintains form file compatibility with 16 bit
Delphi (If loaded as WMF, then save as WMF).
Inch - The units per inch assumed by a WMF metafile. Used to alter
scale when writing as WMF, but otherwise this property is obsolete.
Enhanced metafiles maintain complete scale information internally.
MMWidth,
MMHeight: Width and Height in 0.01 millimeter units, the native
scale used by enhanced metafiles. The Width and Height properties
are always in screen device pixel units; you can avoid loss of
precision in converting between device pixels and mm by setting
or reading the dimentions in mm with these two properties.
CreatedBy - Optional name of the author or application used to create
the metafile.
Description - Optional text description of the metafile.
You can set the CreatedBy and Description of a new metafile by calling
TMetafileCanvas.CreateWithComment.
TMetafileCanvas
To create a metafile image from scratch, you must draw the image in
a metafile canvas. When the canvas is destroyed, it transfers the
image into the metafile object provided to the canvas constructor.
After the image is drawn on the canvas and the canvas is destroyed,
the image is 'playable' in the metafile object. Like this:
MyMetafile := TMetafile.Create;
with TMetafileCanvas.Create(MyMetafile, 0) do
try
Brush.Color := clRed;
Ellipse(0,0,100,100);
...
finally
Free;
end;
Form1.Canvas.Draw(0,0,MyMetafile); (* 1 red circle *)
To add to an existing metafile image, create a metafile canvas
and play the source metafile into the metafile canvas. Like this:
(* continued from previous example, so MyMetafile contains an image *)
with TMetafileCanvas.Create(MyMetafile, 0) do
try
Draw(0,0,MyMetafile);
Brush.Color := clBlue;
Ellipse(100,100,200,200);
...
finally
Free;
end;
Form1.Canvas.Draw(0,0,MyMetafile); (* 1 red circle and 1 blue circle *)
}
TMetafileCanvas = class(TCanvas)
private
FMetafile: TMetafile;
public
constructor Create(AMetafile: TMetafile; ReferenceDevice: HDC);
constructor CreateWithComment(AMetafile: TMetafile; ReferenceDevice: HDC;
const CreatedBy, Description: String);
destructor Destroy; override;
end;
TMetafileImage = class
private
FRefCount: Integer;
FHandle: HENHMETAFILE;
FWidth: Integer; // FWidth and FHeight are in 0.01 mm logical pixels
FHeight: Integer; // These are converted to device pixels in TMetafile
FPalette: HPALETTE;
FInch: Word; // Used only when writing WMF files.
FTempWidth: Integer; // FTempWidth and FTempHeight are in device pixels
FTempHeight: Integer; // Used only when width/height are set when FHandle = 0
procedure Reference;
procedure Release;
end;
TMetafile = class(TGraphic)
private
FImage: TMetafileImage;
FEnhanced: Boolean;
function GetAuthor: String;
function GetDesc: String;
function GetHandle: HENHMETAFILE;
function GetInch: Word;
function GetMMHeight: Integer;
function GetMMWidth: Integer;
function GetPalette: HPALETTE;
procedure NewImage;
procedure SetHandle(Value: HENHMETAFILE);
procedure SetInch(Value: Word);
procedure SetMMHeight(Value: Integer);
procedure SetMMWidth(Value: Integer);
procedure UniqueImage;
protected
function GetEmpty: Boolean; override;
function GetHeight: Integer; override;
function GetWidth: Integer; override;
procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
procedure ReadData(Stream: TStream); override;
procedure ReadEMFStream(Stream: TStream);
procedure ReadWMFStream(Stream: TStream; Length: Longint);
procedure SetHeight(Value: Integer); override;
procedure SetWidth(Value: Integer); override;
function TestEMF(Stream: TStream): Boolean;
procedure WriteData(Stream: TStream); override;
procedure WriteEMFStream(Stream: TStream);
procedure WriteWMFStream(Stream: TStream);
public
constructor Create; override;
destructor Destroy; override;
procedure Clear;
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToFile(const Filename: String); override;
procedure SaveToStream(Stream: TStream); override;
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE); override;
procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE); override;
procedure Assign(Source: TPersistent); override;
property CreatedBy: String read GetAuthor;
property Description: String read GetDesc;
property Enhanced: Boolean read FEnhanced write FEnhanced default True;
property Handle: HENHMETAFILE read GetHandle write SetHandle;
property MMWidth: Integer read GetMMWidth write SetMMWidth;
property MMHeight: Integer read GetMMHeight write SetMMHeight;
property Inch: Word read GetInch write SetInch;
property Palette: HPALETTE read GetPalette;
end;
{ TBitmap }
{ TBitmap is an encapsulation of a Windows HBITMAP and HPALETTE. It manages
the palette realizing automatically as well as having a Canvas to allow
modifications to the palette. Creating copies of a TBitmap is very fast
since the handles is copied not the image. If the image is modified, and
the handle is shared by more than one TBitmap object, the image is copied
before the modification is performed (i.e. copy on write).
Canvas - Allows drawing on the bitmap.
Handle - The HBITMAP encapsulated by the TBitmap. Grabbing the handle
directly should be avoided since it causes the HBITMAP to be copied if
more than one TBitmap share the handle.
Palette - The HPALETTE realized by the TBitmap. Grabbing this handle
directly should be avoided since it causes the HPALETTE to be copied if
more than one TBitmap share the handle.
Monochrome - True if the bitmap is a monochrome bitmap }
TInternalImage = class
private
FRefCount: Integer;
FMemoryImage: TCustomMemoryStream;
procedure Reference;
procedure Release;
procedure FreeHandle; virtual; abstract;
end;
TDIBType = (dtNone, dtWin, dtPM);
TBitmapImage = class(TInternalImage)
private
FHandle: HBITMAP;
FPalette: HPALETTE;
FWidth: Integer;
FHeight: Integer;
FDIBHeader: Pointer;
FDIBBits: Pointer;
FMonochrome: Boolean;
FDIBType: TDIBType;
procedure FreeHandle; override;
end;
TBitmap = class(TGraphic)
private
FImage: TBitmapImage;
FCanvas: TCanvas;
FIgnorePalette: Boolean;
procedure Changing(Sender: TObject);
procedure CopyImage(AHandle: HBITMAP; APalette: HPALETTE; AWidth,
AHeight: Integer; AMonochrome: Boolean);
procedure FreeContext;
function GetCanvas: TCanvas;
function GetHandle: HBITMAP; virtual;
function GetMonochrome: Boolean;
function GetPalette: HPALETTE;
function GetTransparentColor: TColor;
procedure HandleNeeded;
procedure ReadStream(Size: Longint; Stream: TStream);
procedure ReadStreamDIB(Image: TCustomMemoryStream);
procedure SetHandle(Value: HBITMAP);
procedure SetMonochrome(Value: Boolean);
procedure SetPalette(Value: HPALETTE);
procedure MemoryImageNeeded;
procedure PaletteNeeded;
procedure NewImage(NewHandle: HBITMAP; NewPalette: HPALETTE; NewWidth,
NewHeight: Integer; NewMonochrome: Boolean; NewImage: TCustomMemoryStream;
NewDIBType: TDIBType; NewDIBHeader, NewDIBBits: Pointer);
procedure WriteStream(Stream: TStream; WriteSize: Boolean);
protected
procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
function GetEmpty: Boolean; override;
function GetHeight: Integer; override;
function GetWidth: Integer; override;
procedure ReadData(Stream: TStream); override;
procedure SetWidth(Value: Integer); override;
procedure SetHeight(Value: Integer); override;
procedure WriteData(Stream: TStream); override;
public
constructor Create; override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure Dormant;
procedure FreeImage;
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE); override;
procedure LoadFromStream(Stream: TStream); override;
procedure LoadFromResourceName(Instance: THandle; const ResName: String);
procedure LoadFromResourceID(Instance: THandle; ResID: Integer);
function ReleaseHandle: HBITMAP;
function ReleasePalette: HPALETTE;
procedure SaveToClipboardFormat(var Format: Word; var Data: THandle;
var APalette: HPALETTE); override;
procedure SaveToStream(Stream: TStream); override;
property Canvas: TCanvas read GetCanvas;
property Handle: HBITMAP read GetHandle write SetHandle;
property Monochrome: Boolean read GetMonochrome write SetMonochrome;
property Palette: HPALETTE read GetPalette write SetPalette;
property IgnorePalette: Boolean read FIgnorePalette write FIgnorePalette;
property TransparentColor: TColor read GetTransparentColor;
end;
{ TIcon }
{ TIcon encapsulates window HICON handle. Drawing of an icon does not stretch
so calling stretch draw is not meaningful.
Handle - The HICON used by the TIcon. }
TIconImage = class(TInternalImage)
private
FHandle: HICON;
procedure FreeHandle; override;
end;
TIcon = class(TGraphic)
private
FImage: TIconImage;
function GetHandle: HICON;
procedure HandleNeeded;
procedure ImageNeeded;
procedure NewImage(NewHandle: HICON; NewImage: TMemoryStream);
procedure SetHandle(Value: HICON);
protected
procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
function GetEmpty: Boolean; override;
function GetHeight: Integer; override;
function GetWidth: Integer; override;
procedure SetHeight(Value: Integer); override;
procedure SetWidth(Value: Integer); override;
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE); override;
procedure SaveToClipboardFormat(var Format: Word; var Data: THandle;
var APalette: HPALETTE); override;
public
constructor Create; override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure LoadFromStream(Stream: TStream); override;
function ReleaseHandle: HICON;
procedure SaveToStream(Stream: TStream); override;
property Handle: HICON read GetHandle write SetHandle;
end;
var // New TFont instances are intialized with the values in this structure:
DefFontData: TFontData = (
Handle: 0;
Height: 0;
Pitch: fpDefault;
Style: [];
Charset: DEFAULT_CHARSET;
Name: 'MS Sans Serif');
function GraphicFilter(GraphicClass: TGraphicClass): string;
function GraphicExtension(GraphicClass: TGraphicClass): string;
function ColorToRGB(Color: TColor): Longint;
function ColorToString(Color: TColor): string;
function StringToColor(const S: string): TColor;
procedure GetColorValues(Proc: TGetStrProc);
function ColorToIdent(Color: Longint; var Ident: string): Boolean;
function IdentToColor(const Ident: string; var Color: Longint): Boolean;
procedure GetCharsetValues(Proc: TGetStrProc);
function CharsetToIdent(Charset: Longint; var Ident: string): Boolean;
function IdentToCharset(const Ident: string; var Charset: Longint): Boolean;
function GetDefFontCharSet: TFontCharSet;
function MemAlloc(Size: Longint): Pointer;
procedure GetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer;
var ImageSize: DWORD);
function GetDIB(Bitmap: HBITMAP; Palette: HPALETTE; var BitmapInfo; var Bits): Boolean;
function CopyPalette(Palette: HPALETTE): HPALETTE;
procedure InitGraphics;
procedure PaletteChanged;
procedure FreeMemoryContexts;
implementation
{ Things left out
---------------
Regions
PatBlt
Tabbed text
Clipping regions
Coordinate transformations
Paths
Beziers }
uses Controls, Forms, Consts;
const
csAllValid = [csHandleValid..csBrushValid];
var
ScreenLogPixels: Integer;
StockPen: HPEN;
StockBrush: HBRUSH;
StockFont: HFONT;
StockIcon: HICON;
{ Resource managers }
const
ResInfoSize = SizeOf(TResource) - SizeOf(TFontData);
type
TResourceManager = class(TObject)
ResList: PResource;
ResDataSize: Word;
constructor Create(AResDataSize: Word);
function AllocResource(const ResData): PResource;
procedure FreeResource(Resource: PResource);
procedure ChangeResource(GraphicsObject: TGraphicsObject; const ResData);
procedure AssignResource(GraphicsObject: TGraphicsObject;
AResource: PResource);
end;
var
FontManager: TResourceManager;
PenManager: TResourceManager;
BrushManager: TResourceManager;
function GetHashCode(const Buffer; Count: Integer): Word; assembler;
asm
MOV ECX,EDX
MOV EDX,EAX
XOR EAX,EAX
@@1: ROL AX,5
XOR AL,[EDX]
INC EDX
DEC ECX
JNE @@1
end;
function BlockCompare(const Buf1, Buf2; Count: Integer): Boolean; {assembler;}
type
BufArray = array[0..MaxInt - 1] of Char;
var
I: Integer;
begin
Result := False;
for I := 0 to Count - 1 do
if BufArray(Buf1)[I] <> BufArray(Buf2)[I] then Exit;
Result := True;
end;
{asm
PUSH ESI
PUSH EDI
MOV ESI,EAX
MOV EDI,EDX
XOR EAX,EAX
CLD
REPE CMPSB
JNE @@1
INC EAX
@@1: POP EDI
POP ESI
end;}
constructor TResourceManager.Create(AResDataSize: Word);
begin
ResDataSize := AResDataSize;
end;
function TResourceManager.AllocResource(const ResData): PResource;
var
ResHash: Word;
begin
ResHash := GetHashCode(ResData, ResDataSize);
Result := ResList;
while (Result <> nil) and ((Result^.HashCode <> ResHash) or
not BlockCompare(Result^.Data, ResData, ResDataSize)) do
Result := Result^.Next;
if Result = nil then
begin
GetMem(Result, ResDataSize + ResInfoSize);
with Result^ do
begin
Next := ResList;
RefCount := 0;
Handle := TResData(ResData).Handle;
HashCode := ResHash;
Move(ResData, Data, ResDataSize);
end;
ResList := Result;
end;
Inc(Result^.RefCount);
end;
procedure TResourceManager.FreeResource(Resource: PResource);
var
P: PResource;
begin
if Resource <> nil then
with Resource^ do
begin
Dec(RefCount);
if RefCount = 0 then
begin
if Handle <> 0 then DeleteObject(Handle);
if Resource = ResList then ResList := Resource^.Next else
begin
P := ResList;
while P^.Next <> Resource do P := P^.Next;
P^.Next := Resource^.Next;
end;
FreeMem(Resource, ResDataSize + ResInfoSize);
end;
end;
end;
procedure TResourceManager.ChangeResource(GraphicsObject: TGraphicsObject;
const ResData);
var
P: PResource;
begin
P := GraphicsObject.FResource;
GraphicsObject.FResource := AllocResource(ResData);
if GraphicsObject.FResource <> P then GraphicsObject.Changed;
FreeResource(P);
end;
procedure TResourceManager.AssignResource(GraphicsObject: TGraphicsObject;
AResource: PResource);
var
P: PResource;
begin
P := GraphicsObject.FResource;
if P <> AResource then
begin
Inc(AResource^.RefCount);
GraphicsObject.FResource := AResource;
GraphicsObject.Changed;
FreeResource(P);
end;
end;
var
CanvasList: TList;
procedure PaletteChanged;
var
I: Integer;
procedure ClearColor(Resource: PResource);
begin
while Resource <> nil do
begin
with Resource^ do
{ Assumes Pen.Color and Brush.Color share the same location }
if (Handle <> 0) and (Pen.Color < 0) then
begin
DeleteObject(Handle);
Handle := 0;
end;
Resource := Resource^.Next;
end;
end;
begin
{ Called when the system palette has changed (WM_SYSCOLORCHANGE) }
for I := 0 to CanvasList.Count - 1 do
TCanvas(CanvasList[I]).DeselectHandles;
ClearColor(PenManager.ResList);
ClearColor(BrushManager.ResList);
end;
{ Color mapping routines }
type
TColorEntry = record
Value: TColor;
Name: string;
end;
const
Colors: array[0..41] of TColorEntry = (
(Value: clBlack; Name: 'clBlack'),
(Value: clMaroon; Name: 'clMaroon'),
(Value: clGreen; Name: 'clGreen'),
(Value: clOlive; Name: 'clOlive'),
(Value: clNavy; Name: 'clNavy'),
(Value: clPurple; Name: 'clPurple'),
(Value: clTeal; Name: 'clTeal'),
(Value: clGray; Name: 'clGray'),
(Value: clSilver; Name: 'clSilver'),
(Value: clRed; Name: 'clRed'),
(Value: clLime; Name: 'clLime'),
(Value: clYellow; Name: 'clYellow'),
(Value: clBlue; Name: 'clBlue'),
(Value: clFuchsia; Name: 'clFuchsia'),
(Value: clAqua; Name: 'clAqua'),
(Value: clWhite; Name: 'clWhite'),
(Value: clScrollBar; Name: 'clScrollBar'),
(Value: clBackground; Name: 'clBackground'),
(Value: clActiveCaption; Name: 'clActiveCaption'),
(Value: clInactiveCaption; Name: 'clInactiveCaption'),
(Value: clMenu; Name: 'clMenu'),
(Value: clWindow; Name: 'clWindow'),
(Value: clWindowFrame; Name: 'clWindowFrame'),
(Value: clMenuText; Name: 'clMenuText'),
(Value: clWindowText; Name: 'clWindowText'),
(Value: clCaptionText; Name: 'clCaptionText'),
(Value: clActiveBorder; Name: 'clActiveBorder'),
(Value: clInactiveBorder; Name: 'clInactiveBorder'),
(Value: clAppWorkSpace; Name: 'clAppWorkSpace'),
(Value: clHighlight; Name: 'clHighlight'),
(Value: clHighlightText; Name: 'clHighlightText'),
(Value: clBtnFace; Name: 'clBtnFace'),
(Value: clBtnShadow; Name: 'clBtnShadow'),
(Value: clGrayText; Name: 'clGrayText'),
(Value: clBtnText; Name: 'clBtnText'),
(Value: clInactiveCaptionText; Name: 'clInactiveCaptionText'),
(Value: clBtnHighlight; Name: 'clBtnHighlight'),
(Value: cl3DDkShadow; Name: 'cl3DDkShadow'),
(Value: cl3DLight; Name: 'cl3DLight'),
(Value: clInfoText; Name: 'clInfoText'),
(Value: clInfoBk; Name: 'clInfoBk'),
(Value: clNone; Name: 'clNone'));
function ColorToRGB(Color: TColor): Longint;
begin
if Color < 0 then
Result := GetSysColor(Color and $000000FF) else
Result := Color;
end;
function ColorToString(Color: TColor): string;
begin
if not ColorToIdent(Color, Result) then
FmtStr(Result, '0x%.8x', [Color]);
end;
function StringToColor(const S: string): TColor;
begin
if not IdentToColor(S, Longint(Result)) then
Result := TColor(StrToInt(S));
end;
procedure GetColorValues(Proc: TGetStrProc);
var
I: Integer;
begin
for I := Low(Colors) to High(Colors) do Proc(Colors[I].Name);
end;
function ColorToIdent(Color: Longint; var Ident: string): Boolean;
var
I: Integer;
begin
for I := Low(Colors) to High(Colors) do
if Colors[I].Value = Color then
begin
Result := True;
Ident := Colors[I].Name;
Exit;
end;
Result := False;
end;
function IdentToColor(const Ident: string; var Color: Longint): Boolean;
var
I: Integer;
begin
for I := Low(Colors) to High(Colors) do
if AnsiCompareText(Colors[I].Name, Ident) = 0 then
begin
Result := True;
Color := Colors[I].Value;
Exit;
end;
Result := False;
end;
{ TGraphicsObject }
procedure TGraphicsObject.Changed;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
{ TFont }
type
TFontCharsetEntry = record
Value: TFontCharset;
Name: string;
end;
const
FontCharsets: array[0..17] of TFontCharsetEntry = (
(Value: 0; Name: 'ANSI_CHARSET'),
(Value: 1; Name: 'DEFAULT_CHARSET'),
(Value: 2; Name: 'SYMBOL_CHARSET'),
(Value: 77; Name: 'MAC_CHARSET'),
(Value: 128; Name: 'SHIFTJIS_CHARSET'),
(Value: 129; Name: 'HANGEUL_CHARSET'),
(Value: 130; Name: 'JOHAB_CHARSET'),
(Value: 134; Name: 'GB2312_CHARSET'),
(Value: 136; Name: 'CHINESEBIG5_CHARSET'),
(Value: 161; Name: 'GREEK_CHARSET'),
(Value: 162; Name: 'TURKISH_CHARSET'),
(Value: 177; Name: 'HEBREW_CHARSET'),
(Value: 178; Name: 'ARABIC_CHARSET'),
(Value: 186; Name: 'BALTIC_CHARSET'),
(Value: 204; Name: 'RUSSIAN_CHARSET'),
(Value: 222; Name: 'THAI_CHARSET'),
(Value: 238; Name: 'EASTEUROPE_CHARSET'),
(Value: 255; Name: 'OEM_CHARSET'));
procedure GetCharsetValues(Proc: TGetStrProc);
var
I: Integer;
begin
for I := Low(FontCharsets) to High(FontCharsets) do Proc(FontCharsets[I].Name);
end;
function CharsetToIdent(Charset: Longint; var Ident: string): Boolean;
var
I: Integer;
begin
for I := Low(FontCharsets) to High(FontCharsets) do
if FontCharsets[I].Value = Charset then
begin
Result := True;
Ident := FontCharsets[I].Name;
Exit;
end;
Result := False;
end;
function IdentToCharset(const Ident: string; var Charset: Longint): Boolean;
var
I: Integer;
begin
for I := Low(FontCharsets) to High(FontCharsets) do
if CompareText(FontCharsets[I].Name, Ident) = 0 then
begin
Result := True;
Charset := FontCharsets[I].Value;
Exit;
end;
Result := False;
end;
constructor TFont.Create;
begin
FResource := FontManager.AllocResource(DefFontData);
FColor := clWindowText;
FPixelsPerInch := ScreenLogPixels;
end;
destructor TFont.Destroy;
begin
FontManager.FreeResource(FResource);
end;
procedure TFont.Assign(Source: TPersistent);
begin
if Source is TFont then
begin
FontManager.AssignResource(Self, TFont(Source).FResource);
Color := TFont(Source).Color;
if PixelsPerInch <> TFont(Source).PixelsPerInch then
Size := TFont(Source).Size;
Exit;
end;
inherited Assign(Source);
end;
procedure TFont.GetData(var FontData: TFontData);
begin
FontData := FResource^.Font;
FontData.Handle := 0;
end;
procedure TFont.SetData(const FontData: TFontData);
begin
FontManager.ChangeResource(Self, FontData);
end;
procedure TFont.SetColor(Value: TColor);
begin
if FColor <> Value then
begin
FColor := Value;
Changed;
end;
end;
function TFont.GetHandle: HFont;
var
LogFont: TLogFont;
begin
with FResource^ do
begin
if Handle = 0 then
begin
with LogFont do
begin
lfHeight := Font.Height;
lfWidth := 0; { have font mapper choose }
lfEscapement := 0; { only straight fonts }
lfOrientation := 0; { no rotation }
if fsBold in Font.Style then
lfWeight := FW_BOLD
else
lfWeight := FW_NORMAL;
lfItalic := Byte(fsItalic in Font.Style);
lfUnderline := Byte(fsUnderline in Font.Style);
lfStrikeOut := Byte(fsStrikeOut in Font.Style);
lfCharSet := Byte(Font.Charset);
StrPCopy(lfFaceName, Font.Name);
lfQuality := DEFAULT_QUALITY;
{ Everything else as default }
lfOutPrecision := OUT_DEFAULT_PRECIS;
lfClipPrecision := CLIP_DEFAULT_PRECIS;
case Pitch of
fpVariable: lfPitchAndFamily := VARIABLE_PITCH;
fpFixed: lfPitchAndFamily := FIXED_PITCH;
else
lfPitchAndFamily := DEFAULT_PITCH;
end;
end;
Handle := CreateFontIndirect(LogFont);
end;
Result := Handle;
end;
end;
procedure TFont.SetHandle(Value: HFont);
var
FontData: TFontData;
begin
FontData := DefFontData;
FontData.Handle := Value;
SetData(FontData);
end;
function TFont.GetHeight: Integer;
begin
Result := FResource^.Font.Height;
end;
procedure TFont.SetHeight(Value: Integer);
var
FontData: TFontData;
begin
GetData(FontData);
FontData.Height := Value;
SetData(FontData);
end;
function TFont.GetName: TFontName;
begin
Result := FResource^.Font.Name;
end;
procedure TFont.SetName(const Value: TFontName);
var
FontData: TFontData;
begin
if Value <> '' then
begin
GetData(FontData);
FillChar(FontData.Name, SizeOf(FontData.Name), 0);
FontData.Name := Value;
SetData(FontData);
end;
end;
function TFont.GetSize: Integer;
begin
Result := -MulDiv(Height, 72, FPixelsPerInch);
end;
procedure TFont.SetSize(Value: Integer);
begin
Height := -MulDiv(Value, FPixelsPerInch, 72);
end;
function TFont.GetStyle: TFontStyles;
begin
Result := FResource^.Font.Style;
end;
procedure TFont.SetStyle(Value: TFontStyles);
var
FontData: TFontData;
begin
GetData(FontData);
FontData.Style := Value;
SetData(FontData);
end;
function TFont.GetPitch: TFontPitch;
begin
Result := FResource^.Font.Pitch;
end;
procedure TFont.SetPitch(Value: TFontPitch);
var
FontData: TFontData;
begin
GetData(FontData);
FontData.Pitch := Value;
SetData(FontData);
end;
function TFont.GetCharset: TFontCharset;
begin
Result := FResource^.Font.Charset;
end;
procedure TFont.SetCharset(Value: TFontCharset);
var
FontData: TFontData;
begin
GetData(FontData);
FontData.Charset := Value;
SetData(FontData);
end;
{ TPen }
const
DefPenData: TPenData = (
Handle: 0;
Color: clBlack;
Width: 1;
Style: psSolid);
constructor TPen.Create;
begin
FResource := PenManager.AllocResource(DefPenData);
FMode := pmCopy;
end;
destructor TPen.Destroy;
begin
PenManager.FreeResource(FResource);
end;
procedure TPen.Assign(Source: TPersistent);
begin
if Source is TPen then
begin
PenManager.AssignResource(Self, TPen(Source).FResource);
SetMode(TPen(Source).FMode);
Exit;
end;
inherited Assign(Source);
end;
procedure TPen.GetData(var PenData: TPenData);
begin
PenData := FResource^.Pen;
PenData.Handle := 0;
end;
procedure TPen.SetData(const PenData: TPenData);
begin
PenManager.ChangeResource(Self, PenData);
end;
function TPen.GetColor: TColor;
begin
Result := FResource^.Pen.Color;
end;
procedure TPen.SetColor(Value: TColor);
var
PenData: TPenData;
begin
GetData(PenData);
PenData.Color := Value;
SetData(PenData);
end;
function TPen.GetHandle: HPen;
const
PenStyles: array[TPenStyle] of Word =
(PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, PS_NULL,
PS_INSIDEFRAME);
var
LogPen: TLogPen;
begin
with FResource^ do
begin
if Handle = 0 then
begin
with LogPen do
begin
lopnStyle := PenStyles[Pen.Style];
lopnWidth.X := Pen.Width;
lopnColor := ColorToRGB(Pen.Color);
end;
Handle := CreatePenIndirect(LogPen);
end;
Result := Handle;
end;
end;
procedure TPen.SetHandle(Value: HPen);
var
PenData: TPenData;
begin
PenData := DefPenData;
PenData.Handle := Value;
SetData(PenData);
end;
procedure TPen.SetMode(Value: TPenMode);
begin
if FMode <> Value then
begin
FMode := Value;
Changed;
end;
end;
function TPen.GetStyle: TPenStyle;
begin
Result := FResource^.Pen.Style;
end;
procedure TPen.SetStyle(Value: TPenStyle);
var
PenData: TPenData;
begin
GetData(PenData);
PenData.Style := Value;
SetData(PenData);
end;
function TPen.GetWidth: Integer;
begin
Result := FResource^.Pen.Width;
end;
procedure TPen.SetWidth(Value: Integer);
var
PenData: TPenData;
begin
if Value >= 0 then
begin
GetData(PenData);
PenData.Width := Value;
SetData(PenData);
end;
end;
{ TBrush }
const
DefBrushData: TBrushData = (
Handle: 0;
Color: clWhite;
Bitmap: nil;
Style: bsSolid);
constructor TBrush.Create;
begin
FResource := BrushManager.AllocResource(DefBrushData);
end;
destructor TBrush.Destroy;
begin
BrushManager.FreeResource(FResource);
end;
procedure TBrush.Assign(Source: TPersistent);
begin
if Source is TBrush then
begin
BrushManager.AssignResource(Self, TBrush(Source).FResource);
Exit;
end;
inherited Assign(Source);
end;
procedure TBrush.GetData(var BrushData: TBrushData);
begin
BrushData := FResource^.Brush;
BrushData.Handle := 0;
BrushData.Bitmap := nil;
end;
procedure TBrush.SetData(const BrushData: TBrushData);
begin
BrushManager.ChangeResource(Self, BrushData);
end;
function TBrush.GetBitmap: TBitmap;
begin
Result := FResource^.Brush.Bitmap;
end;
procedure TBrush.SetBitmap(Value: TBitmap);
var
BrushData: TBrushData;
begin
BrushData := DefBrushData;
BrushData.Bitmap := Value;
SetData(BrushData);
end;
function TBrush.GetColor: TColor;
begin
Result := FResource^.Brush.Color;
end;
procedure TBrush.SetColor(Value: TColor);
var
BrushData: TBrushData;
begin
GetData(BrushData);
BrushData.Color := Value;
if BrushData.Style = bsClear then BrushData.Style := bsSolid;
SetData(BrushData);
end;
function TBrush.GetHandle: HBrush;
var
LogBrush: TLogBrush;
begin
with FResource^ do
begin
if Handle = 0 then
begin
with LogBrush do
begin
if Brush.Bitmap <> nil then
begin
lbStyle := BS_PATTERN;
lbHatch := Brush.Bitmap.Handle;
end else
begin
lbHatch := 0;
case Brush.Style of
bsSolid: lbStyle := BS_SOLID;
bsClear: lbStyle := BS_HOLLOW;
else
lbStyle := BS_HATCHED;
lbHatch := Ord(Brush.Style) - Ord(bsHorizontal);
end;
end;
lbColor := ColorToRGB(Brush.Color);
end;
Handle := CreateBrushIndirect(LogBrush);
end;
Result := Handle;
end;
end;
procedure TBrush.SetHandle(Value: HBrush);
var
BrushData: TBrushData;
begin
BrushData := DefBrushData;
BrushData.Handle := Value;
SetData(BrushData);
end;
function TBrush.GetStyle: TBrushStyle;
begin
Result := FResource^.Brush.Style;
end;
procedure TBrush.SetStyle(Value: TBrushStyle);
var
BrushData: TBrushData;
begin
GetData(BrushData);
BrushData.Style := Value;
if BrushData.Style = bsClear then BrushData.Color := clWhite;
SetData(BrushData);
end;
{ TCanvas }
constructor TCanvas.Create;
begin
inherited Create;
FFont := TFont.Create;
FFont.OnChange := FontChanged;
FPen := TPen.Create;
FPen.OnChange := PenChanged;
FBrush := TBrush.Create;
FBrush.OnChange := BrushChanged;
FCopyMode := cmSrcCopy;
State := [];
CanvasList.Add(Self);
end;
destructor TCanvas.Destroy;
begin
CanvasList.Remove(Self);
SetHandle(0);
FFont.Free;
FPen.Free;
FBrush.Free;
inherited Destroy;
end;
procedure TCanvas.Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
Windows.Arc(FHandle, X1, Y1, X2, Y2, X3, Y3, X4, Y4);
Changed;
end;
var
MonoBmp: TBitmap = nil;
procedure TCanvas.BrushCopy(const Dest: TRect; Bitmap: TBitmap;
const Source: TRect; Color: TColor);
const
ROP_DSPDxax = $00E20746;
var
crBack, crText: TColorRef;
W, H: Integer;
begin
if Bitmap = nil then Exit;
Changing;
W := Source.Right - Source.Left;
H := Source.Bottom - Source.Top;
RequiredState([csHandleValid]);
{ Build a mask and paint through it }
if not Assigned(MonoBmp) then
begin
MonoBmp := TBitmap.Create;
MonoBmp.Monochrome := True;
end;
if W > MonoBmp.Width then MonoBmp.Width := W;
if H > MonoBmp.Height then MonoBmp.Height := H;
MonoBmp.Canvas.RequiredState([csHandleValid]);
Bitmap.Canvas.RequiredState([csHandleValid]);
crBack := SetBkColor(Bitmap.Canvas.FHandle, ColorToRGB(Color));
BitBlt(MonoBmp.Canvas.FHandle, 0, 0, W, H,
Bitmap.Canvas.FHandle, Source.Left, Source.Top, SrcCopy);
SetBkColor(Bitmap.Canvas.FHandle, crBack);
RequiredState([csHandleValid, csBrushValid]);
StretchBlt(FHandle, Dest.Left, Dest.Top, Dest.Right - Dest.Left,
Dest.Bottom - Dest.Top, Bitmap.Canvas.FHandle, Source.Left, Source.Top,
W, H, SrcCopy);
crText := SetTextColor(FHandle, 0);
crBack := SetBkColor(FHandle, $FFFFFF);
StretchBlt(FHandle, Dest.Left, Dest.Top, Dest.Right - Dest.Left,
Dest.Bottom - Dest.Top, MonoBmp.Canvas.FHandle, 0, 0, W, H, ROP_DSPDxax);
SetTextColor(FHandle, crText);
SetBkColor(FHandle, crBack);
Changed;
end;
procedure TCanvas.Chord(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
Windows.Chord(FHandle, X1, Y1, X2, Y2, X3, Y3, X4, Y4);
Changed;
end;
procedure TCanvas.CopyRect(const Dest: TRect; Canvas: TCanvas;
const Source: TRect);
begin
Changing;
RequiredState([csHandleValid, csFontValid, csBrushValid]);
Canvas.RequiredState([csHandleValid, csBrushValid]);
StretchBlt(FHandle, Dest.Left, Dest.Top, Dest.Right - Dest.Left,
Dest.Bottom - Dest.Top, Canvas.FHandle, Source.Left, Source.Top,
Source.Right - Source.Left, Source.Bottom - Source.Top, CopyMode);
Changed;
end;
procedure TCanvas.Draw(X, Y: Integer; Graphic: TGraphic);
begin
if (Graphic <> nil) and not Graphic.Empty then
begin
Changing;
RequiredState([csHandleValid]);
SetBkColor(FHandle, ColorToRGB(FBrush.Color));
SetTextColor(FHandle, ColorToRGB(FFont.Color));
Graphic.Draw(Self, Rect(X, Y, X + Graphic.Width, Y + Graphic.Height));
Changed;
end;
end;
procedure TCanvas.DrawFocusRect(const Rect: TRect);
begin
Changing;
RequiredState([csHandleValid, csBrushValid]);
Windows.DrawFocusRect(FHandle, Rect);
Changed;
end;
procedure TCanvas.Ellipse(X1, Y1, X2, Y2: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
Windows.Ellipse(FHandle, X1, Y1, X2, Y2);
Changed;
end;
procedure TCanvas.FillRect(const Rect: TRect);
begin
Changing;
RequiredState([csHandleValid, csBrushValid]);
Windows.FillRect(FHandle, Rect, Brush.GetHandle);
Changed;
end;
procedure TCanvas.FloodFill(X, Y: Integer; Color: TColor;
FillStyle: TFillStyle);
const
FillStyles: array[TFillStyle] of Word =
(FLOODFILLSURFACE, FLOODFILLBORDER);
begin
Changing;
RequiredState([csHandleValid, csBrushValid]);
Windows.ExtFloodFill(FHandle, X, Y, Color, FillStyles[FillStyle]);
Changed;
end;
procedure TCanvas.FrameRect(const Rect: TRect);
begin
Changing;
RequiredState([csHandleValid, csBrushValid]);
Windows.FrameRect(FHandle, Rect, Brush.GetHandle);
Changed;
end;
procedure TCanvas.LineTo(X, Y: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
Windows.LineTo(FHandle, X, Y);
Changed;
end;
procedure TCanvas.MoveTo(X, Y: Integer);
begin
RequiredState([csHandleValid]);
Windows.MoveToEx(FHandle, X, Y, nil);
end;
procedure TCanvas.Pie(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
Windows.Pie(FHandle, X1, Y1, X2, Y2, X3, Y3, X4, Y4);
Changed;
end;
type
PPoints = ^TPoints;
TPoints = array[0..0] of TPoint;
procedure TCanvas.Polygon(const Points: array of TPoint);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
Windows.Polygon(FHandle, PPoints(@Points)^, High(Points) + 1);
Changed;
end;
procedure TCanvas.Polyline(const Points: array of TPoint);
begin
Changing;
RequiredState([csHandleValid, csPenValid, csBrushValid]);
Windows.Polyline(FHandle, PPoints(@Points)^, High(Points) + 1);
Changed;
end;
procedure TCanvas.Rectangle(X1, Y1, X2, Y2: Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
Windows.Rectangle(FHandle, X1, Y1, X2, Y2);
Changed;
end;
procedure TCanvas.Refresh;
begin
DeselectHandles;
end;
procedure TCanvas.RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
begin
Changing;
RequiredState([csHandleValid, csBrushValid, csPenValid]);
Windows.RoundRect(FHandle, X1, Y1, X2, Y2, X3, Y3);
Changed;
end;
procedure TCanvas.StretchDraw(const Rect: TRect; Graphic: TGraphic);
begin
if Graphic <> nil then
begin
Changing;
RequiredState(csAllValid);
Graphic.Draw(Self, Rect);
Changed;
end;
end;
procedure TCanvas.TextOut(X, Y: Integer; const Text: String);
begin
Changing;
RequiredState([csHandleValid, csFontValid, csBrushValid]);
Windows.TextOut(FHandle, X, Y, PChar(Text), Length(Text));
MoveTo(X + TextWidth(Text), Y);
Changed;
end;
procedure TCanvas.TextRect(Rect: TRect; X, Y: Integer; const Text: string);
var
Options: Integer;
begin
Changing;
RequiredState([csHandleValid, csFontValid, csBrushValid]);
Options := ETO_CLIPPED;
if Brush.Style <> bsClear then Inc(Options, ETO_OPAQUE);
Windows.ExtTextOut(FHandle, X, Y, Options, @Rect, PChar(Text),
Length(Text), nil);
Changed;
end;
function TCanvas.TextWidth(const Text: String): Integer;
var
Extent: TSize;
begin
RequiredState([csHandleValid, csFontValid]);
if Windows.GetTextExtentPoint(FHandle, PChar(Text), Length(Text), Extent) then
TextWidth := Extent.cX else
TextWidth := 0;
end;
function TCanvas.TextHeight(const Text: String): Integer;
var
Extent: TSize;
begin
RequiredState([csHandleValid, csFontValid]);
if Windows.GetTextExtentPoint(FHandle, PChar(Text), Length(Text), Extent) then
TextHeight := Extent.cY else
TextHeight := 0;
end;
procedure TCanvas.SetFont(Value: TFont);
begin
FFont.Assign(Value);
end;
procedure TCanvas.SetPen(Value: TPen);
begin
FPen.Assign(Value);
end;
procedure TCanvas.SetBrush(Value: TBrush);
begin
FBrush.Assign(Value);
end;
function TCanvas.GetPenPos: TPoint;
begin
RequiredState([csHandleValid]);
Windows.GetCurrentPositionEx(FHandle, @Result);
end;
procedure TCanvas.SetPenPos(Value: TPoint);
begin
MoveTo(Value.X, Value.Y);
end;
function TCanvas.GetPixel(X, Y: Integer): TColor;
begin
RequiredState([csHandleValid]);
GetPixel := Windows.GetPixel(FHandle, X, Y);
end;
procedure TCanvas.SetPixel(X, Y: Integer; Value: TColor);
begin
Changing;
RequiredState([csHandleValid, csPenValid]);
Windows.SetPixel(FHandle, X, Y, ColorToRGB(Value));
Changed;
end;
function TCanvas.GetClipRect: TRect;
begin
RequiredState([csHandleValid]);
GetClipBox(FHandle, Result);
end;
function TCanvas.GetHandle: HDC;
begin
Changing;
RequiredState(csAllValid);
Result := FHandle;
end;
procedure TCanvas.DeselectHandles;
begin
if (FHandle <> 0) and (State - [csPenValid, csBrushValid, csFontValid] <> State) then
begin
SelectObject(FHandle, StockPen);
SelectObject(FHandle, StockBrush);
SelectObject(FHandle, StockFont);
State := State - [csPenValid, csBrushValid, csFontValid];
end;
end;
procedure TCanvas.CreateHandle;
begin
end;
procedure TCanvas.SetHandle(Value: HDC);
begin
if FHandle <> Value then
begin
if FHandle <> 0 then
begin
DeselectHandles;
FPenPos := GetPenPos;
FHandle := 0;
Exclude(State, csHandleValid);
end;
if Value <> 0 then
begin
Include(State, csHandleValid);
FHandle := Value;
SetPenPos(FPenPos);
end;
end;
end;
procedure TCanvas.RequiredState(ReqState: TCanvasState);
var
NeededState: TCanvasState;
begin
NeededState := ReqState - State;
if NeededState <> [] then
begin
if csHandleValid in NeededState then
begin
CreateHandle;
if FHandle = 0 then
raise EInvalidOperation.CreateRes(SNoCanvasHandle);
end;
if csFontValid in NeededState then CreateFont;
if csPenValid in NeededState then
begin
CreatePen;
if Pen.Style in [psDash, psDot, psDashDot, psDashDotDot] then
Include(NeededState, csBrushValid);
end;
if csBrushValid in NeededState then CreateBrush;
State := State + NeededState;
end;
end;
procedure TCanvas.Changing;
begin
if Assigned(FOnChanging) then FOnChanging(Self);
end;
procedure TCanvas.Changed;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TCanvas.CreateFont;
begin
SelectObject(FHandle, Font.GetHandle);
SetTextColor(FHandle, ColorToRGB(Font.Color));
end;
procedure TCanvas.CreatePen;
const
PenModes: array[TPenMode] of Word =
(R2_BLACK, R2_WHITE, R2_NOP, R2_NOT, R2_COPYPEN, R2_NOTCOPYPEN, R2_MERGEPENNOT,
R2_MASKPENNOT, R2_MERGENOTPEN, R2_MASKNOTPEN, R2_MERGEPEN, R2_NOTMERGEPEN,
R2_MASKPEN, R2_NOTMASKPEN, R2_XORPEN, R2_NOTXORPEN);
begin
SelectObject(FHandle, Pen.GetHandle);
SetROP2(FHandle, PenModes[Pen.Mode]);
end;
procedure TCanvas.CreateBrush;
begin
UnrealizeObject(Brush.Handle);
SelectObject(FHandle, Brush.Handle);
if Brush.Style = bsSolid then
begin
SetBkColor(FHandle, ColorToRGB(Brush.Color));
SetBkMode(FHandle, OPAQUE);
end
else
begin
{ Win95 doesn't draw brush hatches if bkcolor = brush color }
{ Since bkmode is transparent, nothing should use bkcolor anyway }
SetBkColor(FHandle, not ColorToRGB(Brush.Color));
SetBkMode(FHandle, TRANSPARENT);
end;
end;
procedure TCanvas.FontChanged(AFont: TObject);
begin
if csFontValid in State then
begin
Exclude(State, csFontValid);
SelectObject(FHandle, StockFont);
end;
end;
procedure TCanvas.PenChanged(APen: TObject);
begin
if csPenValid in State then
begin
Exclude(State, csPenValid);
SelectObject(FHandle, StockPen);
end;
end;
procedure TCanvas.BrushChanged(ABrush: TObject);
begin
if csBrushValid in State then
begin
Exclude(State, csBrushValid);
SelectObject(FHandle, StockBrush);
end;
end;
{ Picture support }
{ Icon and cursor types }
const
rc3_StockIcon = 0;
rc3_Icon = 1;
rc3_Cursor = 2;
type
PCursorOrIcon = ^TCursorOrIcon;
TCursorOrIcon = packed record
Reserved: Word;
wType: Word;
Count: Word;
end;
PIconRec = ^TIconRec;
TIconRec = packed record
Width: Byte;
Height: Byte;
Colors: Word;
Reserved1: Word;
Reserved2: Word;
DIBSize: Longint;
DIBOffset: Longint;
end;
{ Metafile types }
const
WMFKey = $9AC6CDD7;
WMFWord = $CDD7;
type
PMetafileHeader = ^TMetafileHeader;
TMetafileHeader = packed record
Key: Longint;
Handle: SmallInt;
Box: TSmallRect;
Inch: Word;
Reserved: Longint;
CheckSum: Word;
end;
{ Exception routines }
procedure InvalidOperation(Str: Integer); near;
begin
raise EInvalidGraphicOperation.CreateRes(Str);
end;
procedure InvalidGraphic(Str: Integer); near;
begin
raise EInvalidGraphic.CreateRes(Str);
end;
procedure InvalidBitmap; near;
begin
InvalidGraphic(SInvalidBitmap);
end;
procedure InvalidIcon; near;
begin
InvalidGraphic(SInvalidIcon);
end;
procedure InvalidMetafile; near;
begin
InvalidGraphic(SInvalidMetafile);
end;
procedure OutOfResources; near;
begin
raise EOutOfResources.CreateRes(SOutOfResources);
end;
function MemAlloc(Size: Longint): Pointer;
begin
GetMem(Result, Size);
end;
function DupBits(Src: HBITMAP; Size: TPoint; Mono: Boolean): HBITMAP;
var
DC, Mem1, Mem2: HDC;
Old1, Old2: HBITMAP;
Bitmap: Windows.TBitmap;
begin
Mem1 := CreateCompatibleDC(0);
Mem2 := CreateCompatibleDC(0);
GetObject(Src, SizeOf(Bitmap), @Bitmap);
if Mono then
Result := CreateBitmap(Size.X, Size.Y, 1, 1, nil)
else
begin
DC := GetDC(0);
if DC = 0 then OutOfResources;
try
Result := CreateCompatibleBitmap(DC, Size.X, Size.Y);
if Result = 0 then OutOfResources;
finally
ReleaseDC(0, DC);
end;
end;
if Result <> 0 then
begin
Old1 := SelectObject(Mem1, Src);
Old2 := SelectObject(Mem2, Result);
StretchBlt(Mem2, 0, 0, Size.X, Size.Y, Mem1, 0, 0, Bitmap.bmWidth,
Bitmap.bmHeight, SrcCopy);
if Old1 <> 0 then SelectObject(Mem1, Old1);
if Old2 <> 0 then SelectObject(Mem2, Old2);
end;
DeleteDC(Mem1);
DeleteDC(Mem2);
end;
function GetDInColors(BitCount: Word): Integer;
begin
case BitCount of
1, 4, 8: Result := 1 shl BitCount;
else
Result := 0;
end;
end;
function PaletteFromW3DIB(const BI: TBitmapInfo): HPALETTE;
var
DstPal: PLogPalette;
Colors, n: Integer;
Size: Longint;
DC: HDC;
Focus: HWND;
SysPalSize: Integer;
I: Integer;
begin
Result := 0;
{ If the ClrUsed field of the header is non-zero, it means that we could
have a short color table }
with BI.bmiHeader do
if biClrUsed <> 0 then
Colors := biClrUsed
else
Colors := GetDInColors(biBitCount);
if Colors <= 2 then Exit;
Size := SizeOf(TLogPalette) + ((Colors - 1) * SizeOf(TPaletteEntry));
DstPal := AllocMem(Size);
try
FillChar(DstPal^, Size, 0);
with DstPal^ do
begin
palNumEntries := Colors;
palVersion := $300;
Focus := GetFocus;
DC := GetDC(Focus);
try
SysPalSize := GetDeviceCaps(DC, SIZEPALETTE);
if (Colors = 16) and (SysPalSize >= 16) then
begin
{ Ignore the disk image of the palette for 16 color bitmaps use
instead the first 8 and last 8 of the current system palette }
GetSystemPaletteEntries(DC, 0, 8, palPalEntry);
I := 8;
GetSystemPaletteEntries(DC, SysPalSize - I, I, palPalEntry[I]);
end
else
{ Copy the palette for all others (i.e. 256 colors) }
for N := 0 to Colors - 1 do
begin
palPalEntry[N].peRed := BI.bmiColors[N].rgbRed;
palPalEntry[N].peGreen := BI.bmiColors[N].rgbGreen;
palPalEntry[N].peBlue := BI.bmiColors[N].rgbBlue;
palPalEntry[N].peFlags := 0;
end;
finally
ReleaseDC(Focus, DC);
end;
end;
Result := CreatePalette(DstPal^);
finally
FreeMem(DstPal, Size);
end;
end;
procedure ReadWin3DIB(Stream: TStream; var Bits: HBITMAP; var Pal: HPALETTE;
HeaderSize: Longint; ImageSize: Longint);
var
Size: Word;
Focus: HWND;
DC: HDC;
BitsMem: Pointer;
BitmapHeader: TBitmapInfoHeader;
BitmapInfo: PBitmapInfo;
OldPal: HPALETTE;
begin
Stream.Read(Pointer(Longint(@BitmapHeader) + SizeOf(Longint))^,
SizeOf(TBitmapInfoHeader) - SizeOf(Longint));
BitmapHeader.biSize := HeaderSize;
{ check number of planes. Windows 3.x supports only 1 plane DIBS }
if BitmapHeader.biPlanes <> 1 then InvalidBitmap;
with BitmapHeader do
begin
if biClrUsed = 0 then
biClrUsed := GetDInColors(biBitCount);
Size := biClrUsed * SizeOf(TRgbQuad);
end;
BitmapInfo := AllocMem(Size + SizeOf(TBitmapInfoHeader));
try
with BitmapInfo^ do
begin
bmiHeader := BitmapHeader;
Stream.Read(bmiColors, Size);
{ now we've got the color table. Create a pallete from it }
Pal := PaletteFromW3DIB(BitmapInfo^);
{ some applications do not fill in the SizeImage field in the header.
(Actually the truth is more likely that some drivers do not fill the field
in and the apps do not compensate for these buggy drivers.) Therefore, if
this field is 0, we will compute the size. }
with bmiHeader do
begin
Dec(ImageSize, SizeOf(TBitmapInfoHeader) + Size);
if biSizeImage <> 0 then
if biSizeImage < ImageSize then ImageSize := biSizeImage;
BitsMem := AllocMem(ImageSize);
try
Stream.Read(BitsMem^, ImageSize);
{ we use the handle of the window with the focus (which, if this routine
is called from a menu command, will be this window) in order to guarantee
that the realized palette will have first priority on the system palette }
Focus := GetFocus;
DC := GetDC(Focus);
if DC = 0 then OutOfResources;
try
if Pal <> 0 then
begin
{ select and realize our palette we have gotten the DC of the focus
window just to make sure that all our colors are mapped }
OldPal := SelectPalette(DC, Pal, False);
RealizePalette(DC);
end
else
OldPal := 0;
try
Bits := CreateDIBitmap(DC, BitmapInfo^.bmiHeader, CBM_INIT, BitsMem,
BitmapInfo^, DIB_RGB_COLORS);
if Bits = 0 then OutOfResources;
finally
if OldPal <> 0 then
SelectPalette(DC, OldPal, False);
end;
finally
ReleaseDC(Focus, DC);
end;
finally
FreeMem(BitsMem, ImageSize);
end;
end;
end;
finally
FreeMem(BitmapInfo, Size + SizeOf(TBitmapInfoHeader));
end;
end;
{ This routine accepts a pointer to a BITMAPCORE structure and creates a GDI
logical palette from the color table which follows it, for 2, 16 and 256
color bitmaps. It returns 0 for all others, including 24-bit DIB's
It differs from the windows DIB routine in two respects:
1) The PM 1.x DIB must have complete color tables, since there is no ClrUsed
field in the header
2) The size of the color table entries is 3 bytes, not 4 bytes. }
function PaletteFromPM1DIB(const BC: TBitmapCoreInfo): HPALETTE;
var
DstPal: PLogPalette;
Colors, N: Integer;
Size: Longint;
begin
Result := 0;
Colors := GetDInColors(BC.bmciHeader.bcBitCount);
if Colors = 0 then Exit;
Size := SizeOf(TLogPalette) + ((Colors - 1) * SizeOf(TPaletteEntry));
DstPal := AllocMem(Size);
FillChar(DstPal^, Size, 0);
try
with DstPal^ do
begin
palNumEntries := Colors;
palVersion := $300;
for N := 0 to Colors - 1 do
begin
palPalEntry[N].peRed := BC.bmciColors[N].rgbtRed;
palPalEntry[N].peGreen := BC.bmciColors[N].rgbtGreen;
palPalEntry[N].peBlue := BC.bmciColors[N].rgbtBlue;
palPalEntry[N].peFlags := 0;
end;
end;
Result := CreatePalette(DstPal^);
finally
FreeMem(DstPal, Size);
end;
end;
{ Read a PM 1.x device independent bitmap. }
procedure ReadPM1DIB(Stream: TStream; var Bits: HBITMAP; var Pal: HPALETTE;
HeaderSize: Longint; ImageSize: Longint);
var
Size: Word;
Focus: HWND;
DC: HDC;
BitsMem: Pointer;
BitmapHeader: TBitmapCoreHeader;
BitmapInfo: PBitmapCoreInfo;
OldPal: HPALETTE;
MaxSize: Longint;
begin
Stream.Read(Pointer(Longint(@BitmapHeader) + SizeOf(HeaderSize))^,
SizeOf(BitmapHeader) - SizeOf(Longint));
BitmapHeader.bcSize := HeaderSize;
if BitmapHeader.bcPlanes <> 1 then InvalidBitmap;
Size := GetDInColors(BitmapHeader.bcBitCount) * SizeOf(TRGBTriple);
BitmapInfo := AllocMem(Size + SizeOf(TBitmapCoreInfo));
try
with BitmapInfo^ do
begin
bmciHeader := BitmapHeader;
Stream.Read(bmciColors, Size);
Pal := PaletteFromPM1DIB(BitmapInfo^);
{ size of image = Width of a scan line * number of scan lines Width = Pixel
Width * bits per pixel rounded to a DWORD boundary }
with bmciHeader do
MaxSize := ((((bcWidth * bcBitCount) + 31) div 32) * 4) * bcHeight;
BitsMem := AllocMem(MaxSize);
try
Stream.Read(BitsMem^, MaxSize);
Focus := GetFocus;
DC := GetDC(Focus);
if DC = 0 then OutOfResources;
try
OldPal := 0;
if Pal <> 0 then
begin
OldPal := SelectPalette(DC, Pal, False);
RealizePalette(DC);
end;
try
Bits := CreateDIBitmap(DC, PBitmapInfoHeader(@bmciHeader)^, CBM_INIT,
BitsMem, PBitmapInfo(BitmapInfo)^, DIB_RGB_COLORS);
if Bits = 0 then OutOfResources;
finally
if OldPal <> 0 then
SelectPalette(DC, OldPal, False);
end;
finally
ReleaseDC(Focus, DC);
end;
finally
FreeMem(BitsMem, MaxSize);
end;
end;
finally
FreeMem(BitmapInfo, Size + SizeOf(TBitmapCoreInfo));
end;
end;
procedure ReadDIB(Stream: TStream; var Bits: HBITMAP; var Pal: HPALETTE;
Size: Longint);
var
HeaderSize: Longint;
begin
Stream.Read(HeaderSize, SizeOf(HeaderSize));
if HeaderSize = SizeOf(TBitmapInfoHeader) then
ReadWin3DIB(Stream, Bits, Pal, HeaderSize, Size)
else if HeaderSize = SizeOf(TBitmapCoreHeader) then
ReadPM1DIB(Stream, Bits, Pal, HeaderSize, Size)
else
InvalidBitmap;
end;
function WidthBytes(I: Longint): Longint;
begin
Result := ((I + 31) div 32) * 4;
end;
function MonoWidthBytes(I: Longint): Longint;
begin
Result := ((I + 15) div 16) * 2;
end;
procedure TwoBitsFromDIB(var BI: TBitmapInfoHeader; var XorBits, AndBits: HBITMAP);
type
PLongArray = ^TLongArray;
TLongArray = array[0..1] of Longint;
var
Temp: HBITMAP;
NumColors: Integer;
DC: HDC;
Bits: Pointer;
Colors: PLongArray;
IconSize: TPoint;
begin
IconSize.X := GetSystemMetrics(SM_CXICON);
IconSize.Y := GetSystemMetrics(SM_CYICON);
with BI do
begin
biHeight := biHeight shr 1; { Size in record is doubled }
biSizeImage := WidthBytes(Longint(biWidth) * biBitCount) * biHeight;
NumColors := GetDInColors(biBitCount);
end;
DC := GetDC(0);
if DC = 0 then OutOfResources;
try
Bits := Pointer(Longint(@BI) + SizeOf(BI) + NumColors * SizeOf(TRGBQuad));
Temp := CreateDIBitmap(DC, BI, CBM_INIT, Bits, PBitmapInfo(@BI)^, DIB_RGB_COLORS);
if Temp = 0 then OutOfResources;
try
XorBits := DupBits(Temp, IconSize, False);
finally
DeleteObject(Temp);
end;
with BI do
begin
Inc(Longint(Bits), biSizeImage);
biBitCount := 1;
biSizeImage := WidthBytes(Longint(biWidth) * biBitCount) * biHeight;
biClrUsed := 2;
biClrImportant := 2;
end;
Colors := Pointer(Longint(@BI) + SizeOf(BI));
Colors^[0] := 0;
Colors^[1] := $FFFFFF;
Temp := CreateDIBitmap(DC, BI, CBM_INIT, Bits, PBitmapInfo(@BI)^, DIB_RGB_COLORS);
if Temp = 0 then OutOfResources;
try
AndBits := DupBits(Temp, IconSize, True);
finally
DeleteObject(Temp);
end;
finally
ReleaseDC(0, DC);
end;
end;
procedure ReadIcon(Stream: TStream; var Icon: HICON; ImageCount: Integer;
StartOffset: Integer);
type
PIconRecArray = ^TIconRecArray;
TIconRecArray = array[0..300] of TIconRec;
var
List: PIconRecArray;
HeaderLen, Length: Integer;
Colors, BitsPerPixel: Word;
C1, C2, N, Index: Integer;
IconSize: TPoint;
DC: HDC;
BI: PBitmapInfoHeader;
ResData: Pointer;
XorBits, AndBits: HBITMAP;
XorInfo, AndInfo: Windows.TBitmap;
XorMem, AndMem: Pointer;
XorLen, AndLen: Integer;
begin
HeaderLen := SizeOf(TIconRec) * ImageCount;
List := AllocMem(HeaderLen);
try
Stream.Read(List^, HeaderLen);
IconSize.X := GetSystemMetrics(SM_CXICON);
IconSize.Y := GetSystemMetrics(SM_CYICON);
DC := GetDC(0);
if DC = 0 then OutOfResources;
try
BitsPerPixel := GetDeviceCaps(DC, PLANES) * GetDeviceCaps(DC, BITSPIXEL);
if BitsPerPixel = 24 then
Colors := 0
else
Colors := 1 shl BitsPerPixel;
finally
ReleaseDC(0, DC);
end;
Index := -1;
{ the following code determines which image most closely matches the
current device. It is not meant to absolutely match Windows
(known broken) algorithm }
C2 := 0;
for N := 0 to ImageCount - 1 do
begin
C1 := List^[N].Colors;
if C1 = Colors then
begin
Index := N;
Break;
end
else if Index = -1 then
begin
if C1 <= Colors then
begin
Index := N;
C2 := List^[N].Colors;
end;
end
else
if C1 > C2 then
Index := N;
end;
if Index = -1 then Index := 0;
with List^[Index] do
begin
BI := AllocMem(DIBSize);
try
Stream.Seek(DIBOffset - (HeaderLen + StartOffset), 1);
Stream.Read(BI^, DIBSize);
TwoBitsFromDIB(BI^, XorBits, AndBits);
GetObject(AndBits, SizeOf(Windows.TBitmap), @AndInfo);
GetObject(XorBits, SizeOf(Windows.TBitmap), @XorInfo);
with AndInfo do
AndLen := bmWidthBytes * bmHeight * bmPlanes;
with XorInfo do
XorLen := bmWidthBytes * bmHeight * bmPlanes;
Length := AndLen + XorLen;
ResData := AllocMem(Length);
try
AndMem := ResData;
with AndInfo do
XorMem := Pointer(Longint(ResData) + AndLen);
GetBitmapBits(AndBits, AndLen, AndMem);
GetBitmapBits(XorBits, XorLen, XorMem);
DeleteObject(XorBits);
DeleteObject(AndBits);
Icon := CreateIcon(HInstance, IconSize.X, IconSize.Y,
XorInfo.bmPlanes, XorInfo.bmBitsPixel, AndMem, XorMem);
if Icon = 0 then OutOfResources;
finally
FreeMem(ResData, Length);
end;
finally
FreeMem(BI, DIBSize);
end;
end;
finally
FreeMem(List, HeaderLen);
end;
end;
function ComputeAldusChecksum(var WMF: TMetafileHeader): Word;
type
PWord = ^Word;
var
pW: PWord;
pEnd: PWord;
begin
Result := 0;
pW := @WMF;
pEnd := @WMF.CheckSum;
while Longint(pW) < Longint(pEnd) do
begin
Result := Result xor pW^;
Inc(Longint(pW), SizeOf(Word));
end;
end;
procedure InitializeBitmapInfoHeader(Bitmap: HBITMAP; var BI: TBitmapInfoHeader;
Colors: Integer);
var
BM: Windows.TBitmap;
begin
GetObject(Bitmap, SizeOf(BM), @BM);
with BI do
begin
biSize := SizeOf(BI);
biWidth := BM.bmWidth;
biHeight := BM.bmHeight;
if Colors <> 0 then
case Colors of
2: biBitCount := 1;
16: biBitCount := 4;
256: biBitCount := 8;
end
else biBitCount := BM.bmBitsPixel * BM.bmPlanes;
biPlanes := 1;
biXPelsPerMeter := 0;
biYPelsPerMeter := 0;
biClrUsed := 0;
biClrImportant := 0;
biCompression := BI_RGB;
if biBitCount in [16, 32] then biBitCount := 24;
biSizeImage := WidthBytes(biWidth * biBitCount) * biHeight;
end;
end;
procedure InternalGetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer;
var ImageSize: DWORD; Colors: Integer);
var
BI: TBitmapInfoHeader;
begin
InitializeBitmapInfoHeader(Bitmap, BI, Colors);
with BI do
begin
case biBitCount of
24: InfoHeaderSize := SizeOf(TBitmapInfoHeader);
else
InfoHeaderSize := SizeOf(TBitmapInfoHeader) + SizeOf(TRGBQuad) *
(1 shl biBitCount);
end;
end;
ImageSize := BI.biSizeImage;
end;
procedure GetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: Integer;
var ImageSize: DWORD);
begin
InternalGetDIBSizes(Bitmap, InfoHeaderSize, ImageSize, 0);
end;
function InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE;
var BitmapInfo; var Bits; Colors: Integer): Boolean;
var
OldPal: HPALETTE;
Focus: HWND;
DC: HDC;
begin
InitializeBitmapInfoHeader(Bitmap, TBitmapInfoHeader(BitmapInfo), Colors);
OldPal := 0;
Focus := GetFocus;
DC := GetDC(Focus);
try
if Palette <> 0 then
begin
OldPal := SelectPalette(DC, Palette, False);
RealizePalette(DC);
end;
Result := GetDIBits(DC, Bitmap, 0, TBitmapInfoHeader(BitmapInfo).biHeight, @Bits,
TBitmapInfo(BitmapInfo), DIB_RGB_COLORS) <> 0;
finally
if OldPal <> 0 then SelectPalette(DC, OldPal, False);
ReleaseDC(Focus, DC);
end;
end;
function GetDIB(Bitmap: HBITMAP; Palette: HPALETTE; var BitmapInfo; var Bits): Boolean;
begin
Result := InternalGetDIB(Bitmap, Palette, BitmapInfo, Bits, 0);
end;
procedure DIBFromBit(Stream: TMemoryStream; Src: HBITMAP;
Pal: HPALETTE; Colors: Integer; var DIBHeader, DIBBits: Pointer);
var
HeaderSize: Integer;
ImageSize: DWORD;
begin
if Src = 0 then InvalidBitmap;
InternalGetDIBSizes(Src, HeaderSize, ImageSize, Colors);
Stream.SetSize(HeaderSize + ImageSize);
DIBHeader := Stream.Memory;
DIBBits := Pointer(Longint(DIBHeader) + HeaderSize);
InternalGetDIB(Src, Pal, DIBHeader^, DIBBits^, Colors);
end;
procedure WinError;
begin
end;
procedure CheckBool(Result: Bool);
begin
if not Result then WinError;
end;
procedure WriteIcon(Stream: TStream; Icon: HICON; WriteLength: Boolean);
var
IconInfo: TIconInfo;
MonoInfoSize, ColorInfoSize: Integer;
MonoBitsSize, ColorBitsSize: DWORD;
MonoInfo, MonoBits, ColorInfo, ColorBits: Pointer;
CI: TCursorOrIcon;
List: TIconRec;
Length: Longint;
begin
FillChar(CI, SizeOf(CI), 0);
FillChar(List, SizeOf(List), 0);
CheckBool(GetIconInfo(Icon, IconInfo));
try
InternalGetDIBSizes(IconInfo.hbmMask, MonoInfoSize, MonoBitsSize, 2);
InternalGetDIBSizes(IconInfo.hbmColor, ColorInfoSize, ColorBitsSize, 16);
MonoInfo := nil;
MonoBits := nil;
ColorInfo := nil;
ColorBits := nil;
try
MonoInfo := AllocMem(MonoInfoSize);
MonoBits := AllocMem(MonoBitsSize);
ColorInfo := AllocMem(ColorInfoSize);
ColorBits := AllocMem(ColorBitsSize);
InternalGetDIB(IconInfo.hbmMask, 0, MonoInfo^, MonoBits^, 2);
InternalGetDIB(IconInfo.hbmColor, 0, ColorInfo^, ColorBits^, 16);
if WriteLength then
begin
Length := SizeOf(CI) + SizeOf(List) + ColorInfoSize +
ColorBitsSize + MonoBitsSize;
Stream.Write(Length, SizeOf(Length));
end;
with CI do
begin
CI.wType := RC3_ICON;
CI.Count := 1;
end;
Stream.Write(CI, SizeOf(CI));
with List, PBitmapInfoHeader(ColorInfo)^ do
begin
Width := biWidth;
Height := biHeight;
Colors := biPlanes * biBitCount;
DIBSize := ColorInfoSize + ColorBitsSize + MonoBitsSize;
DIBOffset := SizeOf(CI) + SizeOf(List);
end;
Stream.Write(List, SizeOf(List));
with PBitmapInfoHeader(ColorInfo)^ do
Inc(biHeight, biHeight); { color height includes mono bits }
Stream.Write(ColorInfo^, ColorInfoSize);
Stream.Write(ColorBits^, ColorBitsSize);
Stream.Write(MonoBits^, MonoBitsSize);
finally
FreeMem(ColorInfo, ColorInfoSize);
FreeMem(ColorBits, ColorBitsSize);
FreeMem(MonoInfo, MonoInfoSize);
FreeMem(MonoBits, MonoBitsSize);
end;
finally
DeleteObject(IconInfo.hbmColor);
DeleteObject(IconInfo.hbmMask);
end;
end;
{ TGraphic }
constructor TGraphic.Create;
begin
inherited Create;
end;
procedure TGraphic.Changed(Sender: TObject);
begin
FModified := True;
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TGraphic.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
begin
if Filer.Ancestor <> nil then
Result := not (Filer.Ancestor is TGraphic) or
not Equals(TGraphic(Filer.Ancestor))
else
Result := not Empty;
end;
begin
Filer.DefineBinaryProperty('Data', ReadData, WriteData, DoWrite);
end;
function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler;
asm
PUSH ESI
PUSH EDI
MOV ESI,P1
MOV EDI,P2
MOV EDX,ECX
XOR EAX,EAX
AND EDX,3
SHR ECX,1
SHR ECX,1
REPE CMPSD
JNE @@2
MOV ECX,EDX
REPE CMPSB
JNE @@2
@@1: INC EAX
@@2: POP EDI
POP ESI
end;
function StreamsEqual(S1, S2: TMemoryStream): Boolean;
begin
Result := (S1.Size = S2.Size) and CompareMem(S1.Memory, S2.Memory, S1.Size);
end;
function TGraphic.Equals(Graphic: TGraphic): Boolean;
var
MyImage, GraphicsImage: TMemoryStream;
begin
Result := (Graphic <> nil) and (ClassType = Graphic.ClassType);
if Empty or Graphic.Empty then
begin
Result := Empty and Graphic.Empty;
Exit;
end;
if Result then
begin
MyImage := TMemoryStream.Create;
try
WriteData(MyImage);
GraphicsImage := TMemoryStream.Create;
try
Graphic.WriteData(GraphicsImage);
Result := StreamsEqual(MyImage, GraphicsImage);
finally
GraphicsImage.Free;
end;
finally
MyImage.Free;
end;
end;
end;
procedure TGraphic.SetModified(Value: Boolean);
begin
if Value then
Changed(Self) else
FModified := False;
end;
procedure TGraphic.LoadFromFile(const Filename: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(Filename, fmOpenRead);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TGraphic.SaveToFile(const Filename: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(Filename, fmCreate);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;
procedure TGraphic.ReadData(Stream: TStream);
begin
LoadFromStream(Stream);
end;
procedure TGraphic.WriteData(Stream: TStream);
begin
SaveToStream(Stream);
end;
{ TPicture }
type
PFileFormat = ^TFileFormat;
TFileFormat = record
GraphicClass: TGraphicClass;
Extension: string;
Description: string;
DescResID: Integer;
Next: PFileFormat;
end;
{ Pre-registered file formats }
const
WMFMetafileFormat: TFileFormat = (
GraphicClass: TMetafile;
Extension: 'wmf';
Description: '';
DescResID: SVMetafiles;
Next: nil);
MetaFileFormat: TFileFormat = (
GraphicClass: TMetafile;
Extension: 'emf';
Description: '';
DescResID: SVEnhMetafiles;
Next: @WMFMetaFileFormat);
IconFormat: TFileFormat = (
GraphicClass: TIcon;
Extension: 'ico';
Description: '';
DescResID: SVIcons;
Next: @MetafileFormat);
BitmapFormat: TFileFormat = (
GraphicClass: TBitmap;
Extension: 'bmp';
Description: '';
DescResID: SVBitmaps;
Next: @IconFormat);
var
FileFormatList: PFileFormat = @BitmapFormat;
type
PClipboardFormat = ^TClipboardFormat;
TClipboardFormat = record
GraphicClass: TGraphicClass;
Format: Word;
Next: PClipboardFormat;
end;
const
WMFMetafileClipFormat: TClipboardFormat = (
GraphicClass: TMetafile;
Format: CF_METAFILEPICT;
Next: nil);
MetafileClipFormat: TClipboardFormat = (
GraphicClass: TMetafile;
Format: CF_ENHMETAFILE;
Next: @WMFMetaFileClipFormat);
BitmapClipFormat: TClipboardFormat = (
GraphicClass: TBitmap;
Format: CF_BITMAP;
Next: @MetafileClipFormat);
// DIBClipFormat: TClipboardFormat = (...
var
ClipboardFormatList: PClipboardFormat = @BitmapClipFormat;
destructor TPicture.Destroy;
begin
FGraphic.Free;
inherited Destroy;
end;
procedure TPicture.AssignTo(Dest: TPersistent);
begin
if Graphic is Dest.ClassType then
Dest.Assign(Graphic)
else
inherited AssignTo(Dest);
end;
procedure TPicture.ForceType(GraphicType: TGraphicClass);
begin
if not (Graphic is GraphicType) then
begin
FGraphic.Free;
FGraphic := nil;
FGraphic := GraphicType.Create;
FGraphic.OnChange := Changed;
Changed(Self);
end;
end;
function TPicture.GetBitmap: TBitmap;
begin
ForceType(TBitmap);
Result := TBitmap(Graphic);
end;
function TPicture.GetIcon: TIcon;
begin
ForceType(TIcon);
Result := TIcon(Graphic);
end;
function TPicture.GetMetafile: TMetafile;
begin
ForceType(TMetafile);
Result := TMetafile(Graphic);
end;
procedure TPicture.SetBitmap(Value: TBitmap);
begin
SetGraphic(Value);
end;
procedure TPicture.SetIcon(Value: TIcon);
begin
SetGraphic(Value);
end;
procedure TPicture.SetMetafile(Value: TMetafile);
begin
SetGraphic(Value);
end;
procedure TPicture.SetGraphic(Value: TGraphic);
var
NewGraphic: TGraphic;
begin
NewGraphic := nil;
if Value <> nil then
begin
NewGraphic := TGraphicClass(Value.ClassType).Create;
NewGraphic.Assign(Value);
NewGraphic.OnChange := Changed;
end;
try
FGraphic.Free;
FGraphic := NewGraphic;
Changed(Self);
except
NewGraphic.Free;
raise;
end;
end;
{ Based on the extension of Filename, create the cooresponding TGraphic class
and call its LoadFromFile method. }
procedure TPicture.LoadFromFile(const Filename: string);
var
Ext: string;
Graphic: PFileFormat;
NewGraphic: TGraphic;
begin
Ext := AnsiLowerCaseFileName(Copy(ExtractFileExt(Filename), 2, Maxint));
Graphic := FileFormatList;
while Graphic <> nil do
with Graphic^ do
begin
if Extension <> Ext then
Graphic := Next
else
begin
NewGraphic := GraphicClass.Create;
try
NewGraphic.LoadFromFile(Filename);
except
NewGraphic.Free;
raise;
end;
FGraphic.Free;
FGraphic := NewGraphic;
FGraphic.OnChange := Changed;
Changed(Self);
Exit;
end;
end;
raise EInvalidGraphic.CreateResFmt(SUnknownExtension, [Ext]);
end;
procedure TPicture.SaveToFile(const Filename: string);
begin
if FGraphic <> nil then FGraphic.SaveToFile(Filename);
end;
procedure TPicture.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
var
NewGraphic: TGraphic;
Graphic: PClipboardFormat;
begin
Graphic := ClipboardFormatList;
while Graphic <> nil do
with Graphic^ do
begin
if AFormat <> Format then
Graphic := Next
else
begin
NewGraphic := GraphicClass.Create;
try
NewGraphic.LoadFromClipboardFormat(AFormat, AData, APalette);
except
NewGraphic.Free;
raise;
end;
FGraphic.Free;
FGraphic := NewGraphic;
FGraphic.OnChange := Changed;
Changed(Self);
Exit;
end;
end;
InvalidGraphic(SUnknownClipboardFormat);
end;
procedure TPicture.SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE);
begin
if FGraphic <> nil then
FGraphic.SaveToClipboardFormat(AFormat, AData, APalette);
end;
class function TPicture.SupportsClipboardFormat(AFormat: Word): Boolean;
var
Graphic: PClipboardFormat;
begin
Result := True;
Graphic := ClipboardFormatList;
while Graphic <> nil do
with Graphic^ do
if AFormat = Format then Exit
else Graphic := Next;
Result := False;
end;
procedure TPicture.Assign(Source: TPersistent);
begin
if Source = nil then
SetGraphic(nil)
else if Source is TPicture then
SetGraphic(TPicture(Source).Graphic)
else if Source is TGraphic then
SetGraphic(TGraphic(Source))
else
inherited Assign(Source);
end;
{ Add AGraphicClass to the list of registered TGraphic classes. }
procedure AppendFileFormat(const Ext, Desc: String; DescID: Integer;
AClass: TGraphicClass);
var
NewRec: PFileFormat;
begin
New(NewRec);
with NewRec^ do
begin
Extension := AnsiLowerCaseFileName(Ext);
GraphicClass := AClass;
Description := Desc;
DescResID := DescID;
Next := FileFormatList;
end;
FileFormatList := NewRec;
end;
class procedure TPicture.RegisterFileFormat(const AExtension,
ADescription: string; AGraphicClass: TGraphicClass);
begin
AppendFileFormat(AExtension, ADescription, 0, AGraphicClass);
end;
class procedure TPicture.RegisterFileFormatRes(const AExtension: String;
ADescriptionResID: Integer; AGraphicClass: TGraphicClass);
begin
AppendFileFormat(AExtension, '', ADescriptionResID, AGraphicClass);
end;
class procedure TPicture.RegisterClipboardFormat(AFormat: Word;
AGraphicClass: TGraphicClass);
var
NewRec: PClipboardFormat;
begin
New(NewRec);
with NewRec^ do
begin
GraphicClass := AGraphicClass;
Format := AFormat;
Next := ClipboardFormatList;
end;
ClipboardFormatList := NewRec;
end;
procedure TPicture.Changed(Sender: TObject);
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TPicture.ReadData(Stream: TStream);
var
CName: string[63];
Format: PFileFormat;
NewGraphic: TGraphic;
begin
with Stream do
begin
Read(CName[0], 1);
Read(CName[1], Integer(CName[0]));
Format := FileFormatList;
while Format <> nil do
with Format^ do
if GraphicClass.ClassName <> CName then Format := Next
else
begin
NewGraphic := GraphicClass.Create;
try
NewGraphic.ReadData(Stream);
except
NewGraphic.Free;
raise;
end;
FGraphic.Free;
FGraphic := NewGraphic;
FGraphic.OnChange := Changed;
Changed(Self);
Exit;
end;
end;
end;
procedure TPicture.WriteData(Stream: TStream);
var
CName: string[63];
begin
with Stream do
begin
CName := Graphic.ClassName;
Write(CName, Length(CName) + 1);
Graphic.WriteData(Stream);
end;
end;
procedure TPicture.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
var
Ancestor: TPicture;
begin
if Filer.Ancestor <> nil then
begin
Result := True;
if Filer.Ancestor is TPicture then
begin
Ancestor := TPicture(Filer.Ancestor);
Result := not ((Graphic = Ancestor.Graphic) or
((Graphic <> nil) and (Ancestor.Graphic <> nil) and
Graphic.Equals(Ancestor.Graphic)));
end;
end
else Result := Graphic <> nil;
end;
begin
Filer.DefineBinaryProperty('Data', ReadData, WriteData, DoWrite);
end;
function TPicture.GetWidth: Integer;
begin
Result := 0;
if FGraphic <> nil then Result := FGraphic.Width;
end;
function TPicture.GetHeight: Integer;
begin
Result := 0;
if FGraphic <> nil then Result := FGraphic.Height;
end;
{ TMetafileImage }
procedure TMetafileImage.Reference;
begin
Inc(FRefCount);
end;
procedure TMetafileImage.Release;
begin
if Assigned(Self) then
begin
Dec(FRefCount);
if FRefCount = 0 then
begin
if FHandle <> 0 then DeleteEnhMetafile(FHandle);
if FPalette <> 0 then DeleteObject(FPalette);
Free;
end;
end;
end;
{ TMetafileCanvas }
constructor TMetafileCanvas.Create(AMetafile: TMetafile; ReferenceDevice: HDC);
begin
CreateWithComment(AMetafile, ReferenceDevice, AMetafile.CreatedBy,
AMetafile.Description);
end;
constructor TMetafileCanvas.CreateWithComment(AMetafile : TMetafile;
ReferenceDevice: HDC; const CreatedBy, Description: String);
var
RefDC: HDC;
R: TRect;
Temp: HDC;
P: PChar;
begin
inherited Create;
FMetafile := AMetafile;
RefDC := ReferenceDevice;
if ReferenceDevice = 0 then RefDC := GetDC(0);
try
if FMetafile.MMWidth = 0 then
if FMetafile.Width = 0 then
FMetafile.MMWidth := GetDeviceCaps(RefDC, HORZSIZE)*100
else
FMetafile.MMWidth := MulDiv(FMetafile.Width,
GetDeviceCaps(RefDC, HORZSIZE)*100, GetDeviceCaps(RefDC, HORZRES));
if FMetafile.MMHeight = 0 then
if FMetafile.Height = 0 then
FMetafile.MMHeight := GetDeviceCaps(RefDC, VERTSIZE)*100
else
FMetafile.MMHeight := MulDiv(FMetafile.Height,
GetDeviceCaps(RefDC, VERTSIZE)*100, GetDeviceCaps(RefDC, VERTRES));
R := Rect(0,0,FMetafile.MMWidth,FMetafile.MMHeight);
if (Length(CreatedBy) > 0) or (Length(Description) > 0) then
P := PChar(CreatedBy+#0+Description+#0#0)
else
P := nil;
Temp := CreateEnhMetafile(RefDC, nil, @R, P);
if Temp = 0 then OutOfResources;
Handle := Temp;
finally
if ReferenceDevice = 0 then ReleaseDC(0, RefDC);
end;
end;
destructor TMetafileCanvas.Destroy;
var
Temp: HDC;
begin
Temp := Handle;
Handle := 0;
FMetafile.Handle := CloseEnhMetafile(Temp);
inherited Destroy;
end;
{ TMetafile }
constructor TMetafile.Create;
begin
inherited Create;
FEnhanced := True;
Assign(nil);
end;
destructor TMetafile.Destroy;
begin
FImage.Release;
inherited Destroy;
end;
procedure TMetafile.Assign(Source: TPersistent);
begin
if (Source = nil) or (Source is TMetafile) then
begin
FImage.Release;
if Assigned(Source) then
begin
FImage := TMetafile(Source).FImage;
FEnhanced := TMetafile(Source).Enhanced;
end
else
begin
FImage := TMetafileImage.Create;
FEnhanced := True;
end;
FImage.Reference;
Changed(Self);
end
else
inherited Assign(Source);
end;
procedure TMetafile.Clear;
begin
NewImage;
end;
procedure TMetafile.Draw(ACanvas: TCanvas; const Rect: TRect);
var
MetaPal, OldPal: HPALETTE;
R: TRect;
begin
if FImage = nil then Exit;
MetaPal := Palette;
OldPal := 0;
if MetaPal <> 0 then
begin
OldPal := SelectPalette(ACanvas.Handle, MetaPal, True);
RealizePalette(ACanvas.Handle);
end;
R := Rect;
Dec(R.Right); // Metafile rect includes right and bottom coords
Dec(R.Bottom);
PlayEnhMetaFile(ACanvas.Handle, FImage.FHandle, R);
if MetaPal <> 0 then
SelectPalette(ACanvas.Handle, OldPal, True);
end;
function TMetafile.GetAuthor: String;
var
Temp: Integer;
begin
Result := '';
if (FImage = nil) or (FImage.FHandle = 0) then Exit;
Temp := GetEnhMetafileDescription(FImage.FHandle, 0, nil);
if Temp <= 0 then Exit;
SetLength(Result, Temp);
GetEnhMetafileDescription(FImage.FHandle, Temp, PChar(Result));
SetLength(Result, StrLen(PChar(Result)));
end;
function TMetafile.GetDesc: String;
var
Temp: Integer;
begin
Result := '';
if (FImage = nil) or (FImage.FHandle = 0) then Exit;
Temp := GetEnhMetafileDescription(FImage.FHandle, 0, nil);
if Temp <= 0 then Exit;
SetLength(Result, Temp);
GetEnhMetafileDescription(FImage.FHandle, Temp, PChar(Result));
Delete(Result, 1, StrLen(PChar(Result)));
SetLength(Result, StrLen(PChar(Result)));
end;
function TMetafile.GetEmpty;
begin
Result := FImage = nil;
end;
function TMetafile.GetHandle: HENHMETAFILE;
begin
if Assigned(FImage) then
Result := FImage.FHandle
else
Result := 0;
end;
function TMetafile.GetHeight: Integer;
var
EMFHeader: TEnhMetaHeader;
begin
if FImage = nil then NewImage;
with FImage do
if FInch = 0 then
if FHandle = 0 then
Result := FTempHeight
else
begin { convert 0.01mm units to referenceDC device pixels }
GetEnhMetaFileHeader(FHandle, Sizeof(EMFHeader), @EMFHeader);
Result := MulDiv(FHeight, { metafile height in 0.01mm }
EMFHeader.szlDevice.cy, { device height in pixels }
EMFHeader.szlMillimeters.cy*100); { device height in mm }
end
else { for WMF files, convert to font dpi based device pixels }
Result := MulDiv(FHeight, Screen.PixelsPerInch, 25400);
end;
function TMetafile.GetInch: Word;
begin
Result := 0;
if FImage <> nil then Result := FImage.FInch;
end;
function TMetafile.GetMMHeight: Integer;
begin
if FImage = nil then NewImage;
Result := FImage.FHeight;
end;
function TMetafile.GetMMWidth: Integer;
begin
if FImage = nil then NewImage;
Result := FImage.FWidth;
end;
function TMetafile.GetPalette: HPALETTE;
var
LogPal: PLogPalette;
Count: Integer;
begin
Result := 0;
if (FImage = nil) or (FImage.FHandle = 0) then Exit;
if FImage.FPalette = 0 then
begin
Count := GetEnhMetaFilePaletteEntries(FImage.FHandle, 0, nil);
if Count = 0 then Exit;
if FImage.FPalette <> 0 then DeleteObject(FImage.FPalette);
GetMem(LogPal, Sizeof(TLogPalette) + Count * Sizeof(TPaletteEntry));
try
LogPal^.palVersion := $300;
LogPal^.palNumEntries := Count;
GetEnhMetaFilePaletteEntries(FImage.FHandle, Count, @LogPal^.palPalEntry);
FImage.FPalette := CreatePalette(LogPal^);
finally
FreeMem(LogPal,Sizeof(TLogPalette) + Count * Sizeof(TPaletteEntry));
end;
end;
Result := FImage.FPalette;
end;
function TMetafile.GetWidth: Integer;
var
EMFHeader: TEnhMetaHeader;
begin
if FImage = nil then NewImage;
with FImage do
if FInch = 0 then
if FHandle = 0 then
Result := FTempWidth
else
begin { convert 0.01mm units to referenceDC device pixels }
GetEnhMetaFileHeader(FHandle, Sizeof(EMFHeader), @EMFHeader);
Result := MulDiv(FWidth, { metafile width in 0.01mm }
EMFHeader.szlDevice.cx, { device width in pixels }
EMFHeader.szlMillimeters.cx*100); { device width in 0.01mm }
end
else { for WMF files, convert to font dpi based device pixels }
Result := MulDiv(FWidth, Screen.PixelsPerInch, 25400);
end;
procedure TMetafile.LoadFromStream(Stream: TStream);
begin
NewImage;
if TestEMF(Stream) then
ReadEMFStream(Stream)
else
ReadWMFStream(Stream, Stream.Size - Stream.Position);
Changed(Self);
end;
procedure TMetafile.NewImage;
begin
FImage.Release;
FImage := TMetafileImage.Create;
FImage.Reference;
end;
procedure TMetafile.ReadData(Stream: TStream);
var
Length: Longint;
begin
Stream.Read(Length, SizeOf(Longint));
if TestEMF(Stream) then
ReadEMFStream(Stream)
else
ReadWMFStream(Stream, Length);
Changed(Self);
end;
procedure TMetafile.ReadEMFStream(Stream: TStream);
var
EnhHeader: TEnhMetaheader;
Buf: PChar;
begin
NewImage;
Stream.ReadBuffer(EnhHeader, Sizeof(EnhHeader));
if EnhHeader.dSignature <> ENHMETA_SIGNATURE then InvalidMetafile;
GetMem(Buf, EnhHeader.nBytes);
with FImage do
try
Move(EnhHeader, Buf^, Sizeof(EnhHeader));
Stream.ReadBuffer(PChar(Buf + Sizeof(EnhHeader))^,
EnhHeader.nBytes - Sizeof(EnhHeader));
FHandle := SetEnhMetafileBits(EnhHeader.nBytes, Buf);
if FHandle = 0 then InvalidMetafile;
FInch := 0;
with EnhHeader.rclFrame do
begin
FWidth := Right - Left; { in 0.01 mm units }
FHeight := Bottom - Top;
end;
Enhanced := True;
finally
FreeMem(Buf, EnhHeader.nBytes);
end;
end;
procedure TMetafile.ReadWMFStream(Stream: TStream; Length: Longint);
var
WMF: TMetafileHeader;
BitMem: Pointer;
MFP: TMetaFilePict;
begin
NewImage;
Stream.Read(WMF, SizeOf(WMF));
if (WMF.Key <> WMFKEY) or (ComputeAldusChecksum(WMF) <> WMF.CheckSum) then
InvalidMetafile;
Dec(Length, SizeOf(WMF));
GetMem(Bitmem, Length);
with FImage do
try
Stream.Read(BitMem^, Length);
FImage.FInch := WMF.Inch;
if WMF.Inch = 0 then WMF.Inch := 96;
FWidth := MulDiv(WMF.Box.Right - WMF.Box.Left,25400,WMF.Inch);
FHeight := MulDiv(WMF.Box.Bottom - WMF.Box.Top,25400,WMF.Inch);
with MFP do
begin
MM := MM_ANISOTROPIC;
xExt := 0;
yExt := 0;
hmf := 0;
end;
FHandle := SetWinMetaFileBits(Length, BitMem, 0, MFP);
if FHandle = 0 then InvalidMetafile;
Enhanced := False;
finally
Freemem(BitMem, Length);
end;
end;
procedure TMetafile.SaveToFile(const Filename: String);
var
SaveEnh: Boolean;
begin
SaveEnh := Enhanced;
if AnsiLowerCaseFileName(ExtractFileExt(Filename)) = '.wmf' then
Enhanced := False; { For 16 bit compatibility }
inherited SaveToFile(Filename);
Enhanced := SaveEnh;
end;
procedure TMetafile.SaveToStream(Stream: TStream);
begin
if FImage <> nil then
if Enhanced then
WriteEMFStream(Stream)
else
WriteWMFStream(Stream);
end;
procedure TMetafile.SetHandle(Value: HENHMETAFILE);
var
EnhHeader: TEnhMetaHeader;
begin
if (Value <> 0) and
(GetEnhMetafileHeader(Value, sizeof(EnhHeader), @EnhHeader) = 0) then
InvalidMetafile;
UniqueImage;
if FImage.FHandle <> 0 then DeleteEnhMetafile(FImage.FHandle);
if FImage.FPalette <> 0 then DeleteObject(FImage.FPalette);
FImage.FPalette := 0;
FImage.FHandle := Value;
FImage.FTempWidth := 0;
FImage.FTempHeight := 0;
if Value <> 0 then
with EnhHeader.rclFrame do
begin
FImage.FWidth := Right - Left;
FImage.FHeight := Bottom - Top;
end;
Changed(Self);
end;
procedure TMetafile.SetHeight(Value: Integer);
var
EMFHeader: TEnhMetaHeader;
begin
if FImage = nil then NewImage;
with FImage do
if FInch = 0 then
if FHandle = 0 then
FTempHeight := Value
else
begin { convert device pixels to 0.01mm units }
GetEnhMetaFileHeader(FHandle, Sizeof(EMFHeader), @EMFHeader);
MMHeight := MulDiv(Value, { metafile height in pixels }
EMFHeader.szlMillimeters.cy*100, { device height in 0.01mm }
EMFHeader.szlDevice.cy); { device height in pixels }
end
else
MMHeight := MulDiv(Value, 25400, Screen.PixelsPerInch);
end;
procedure TMetafile.SetInch(Value: Word);
begin
if FImage = nil then NewImage;
if FImage.FInch <> Value then
begin
UniqueImage;
FImage.FInch := Value;
Changed(Self);
end;
end;
procedure TMetafile.SetMMHeight(Value: Integer);
begin
if FImage = nil then NewImage;
FImage.FTempHeight := 0;
if FImage.FHeight <> Value then
begin
UniqueImage;
FImage.FHeight := Value;
Changed(Self);
end;
end;
procedure TMetafile.SetMMWidth(Value: Integer);
begin
if FImage = nil then NewImage;
FImage.FTempWidth := 0;
if FImage.FWidth <> Value then
begin
UniqueImage;
FImage.FWidth := Value;
Changed(Self);
end;
end;
procedure TMetafile.SetWidth(Value: Integer);
var
EMFHeader: TEnhMetaHeader;
begin
if FImage = nil then NewImage;
with FImage do
if FInch = 0 then
if FHandle = 0 then
FTempWidth := Value
else
begin { convert device pixels to 0.01mm units }
GetEnhMetaFileHeader(FHandle, Sizeof(EMFHeader), @EMFHeader);
MMWidth := MulDiv(Value, { metafile width in pixels }
EMFHeader.szlMillimeters.cx*100, { device width in mm }
EMFHeader.szlDevice.cx); { device width in pixels }
end
else
MMWidth := MulDiv(Value, 25400, Screen.PixelsPerInch);
end;
function TMetafile.TestEMF(Stream: TStream): Boolean;
var
Size: Longint;
Header: TEnhMetaHeader;
begin
Size := Stream.Size - Stream.Position;
if Size > Sizeof(Header) then
begin
Stream.Read(Header, Sizeof(Header));
Stream.Seek(-Sizeof(Header), soFromCurrent);
end;
Result := (Size > Sizeof(Header)) and
(Header.iType = EMR_HEADER) and (Header.dSignature = ENHMETA_SIGNATURE);
end;
procedure TMetafile.UniqueImage;
var
NewImage: TMetafileImage;
begin
if FImage = nil then
Self.NewImage
else
if FImage.FRefCount > 1 then
begin
NewImage:= TMetafileImage.Create;
if FImage.FHandle <> 0 then
NewImage.FHandle := CopyEnhMetafile(FImage.FHandle, nil);
NewImage.FHeight := FImage.FHeight;
NewImage.FWidth := FImage.FWidth;
NewImage.FInch := FImage.FInch;
NewImage.FTempWidth := FImage.FTempWidth;
NewImage.FTempHeight := FImage.FTempHeight;
FImage.Release;
FImage := NewImage;
FImage.Reference;
end;
end;
procedure TMetafile.WriteData(Stream: TStream);
var
SavePos: Longint;
begin
if FImage <> nil then
begin
SavePos := 0;
Stream.Write(SavePos, Sizeof(SavePos));
SavePos := Stream.Position - Sizeof(SavePos);
if Enhanced then
WriteEMFStream(Stream)
else
WriteWMFStream(Stream);
Stream.Seek(SavePos, soFromBeginning);
SavePos := Stream.Size - SavePos;
Stream.Write(SavePos, Sizeof(SavePos));
Stream.Seek(0, soFromEnd);
end;
end;
procedure TMetafile.WriteEMFStream(Stream: TStream);
var
Buf: Pointer;
Length: Longint;
begin
if FImage = nil then Exit;
Length := GetEnhMetaFileBits(FImage.FHandle, 0, nil);
GetMem(Buf, Length);
try
GetEnhMetaFileBits(FImage.FHandle, Length, Buf);
Stream.WriteBuffer(Buf^, Length);
finally
FreeMem(Buf, Length);
end;
end;
procedure TMetafile.WriteWMFStream(Stream: TStream);
var
WMF: TMetafileHeader;
Bits: Pointer;
Length: Longint;
RefDC: HDC;
begin
if FImage = nil then Exit;
FillChar(WMF, SizeOf(WMF), 0);
with FImage do
begin
with WMF do
begin
Key := WMFKEY;
if FInch = 0 then
Inch := 2540 { 2540 0.01mm units per inch }
else
Inch := FInch;
with Box do
begin
Left := 0;
Top := 0;
Right := FWidth;
Bottom := FHeight;
end;
CheckSum := ComputeAldusChecksum(WMF);
end;
RefDC := GetDC(0);
try
Length := GetWinMetaFileBits(FHandle, 0, nil, MM_ANISOTROPIC, RefDC);
GetMem(Bits, Length);
try
if GetWinMetaFileBits(FHandle, Length, Bits, MM_ANISOTROPIC,
RefDC) < Length then OutOfResources;
Stream.WriteBuffer(WMF, SizeOf(WMF));
Stream.WriteBuffer(Bits^, Length);
finally
FreeMem(Bits, Length);
end;
finally
ReleaseDC(0, RefDC);
end;
end;
end;
procedure TMetafile.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
var
EnhHeader: TEnhMetaHeader;
begin
AData := GetClipboardData(CF_ENHMETAFILE); // OS will convert WMF to EMF
if AData = 0 then InvalidGraphic(SUnknownClipboardFormat);
NewImage;
with FImage do
begin
FHandle := CopyEnhMetafile(AData, nil);
GetEnhMetaFileHeader(FHandle, sizeof(EnhHeader), @EnhHeader);
with EnhHeader.rclFrame do
begin
FWidth := Right - Left;
FHeight := Bottom - Top;
end;
FInch := 0;
end;
Enhanced := True;
Changed(Self);
end;
procedure TMetafile.SaveToClipboardFormat(var AFormat: Word; var AData: THandle;
var APalette: HPALETTE);
begin
if FImage = nil then Exit;
AFormat := CF_ENHMETAFILE;
APalette := 0;
AData := CopyEnhMetaFile(FImage.FHandle, nil);
end;
var
BitmapCanvasList: TList = nil;
{ TBitmapCanvas }
{ Create a canvas that gets its DC from the memory DC cache }
type
TBitmapCanvas = class(TCanvas)
private
FBitmap: TBitmap;
FOldBitmap: HBITMAP;
FOldPalette: HPALETTE;
procedure FreeContext;
protected
procedure CreateHandle; override;
public
constructor Create(ABitmap: TBitmap);
destructor Destroy; override;
end;
procedure FreeMemoryContexts;
begin
while BitmapCanvasList.Count > 0 do
TBitmapCanvas(BitmapCanvasList[0]).FreeContext;
end;
procedure DeselectBitmap(AHandle: HBITMAP);
var
I: Integer;
begin
for I := BitmapCanvasList.Count - 1 downto 0 do
with TBitmapCanvas(BitmapCanvasList[I]) do
if (FBitmap <> nil) and (FBitmap.FImage.FHandle = AHandle) then
FreeContext;
end;
constructor TBitmapCanvas.Create(ABitmap: TBitmap);
begin
inherited Create;
FBitmap := ABitmap;
end;
destructor TBitmapCanvas.Destroy;
begin
FreeContext;
inherited Destroy;
end;
procedure TBitmapCanvas.FreeContext;
var
H: HBITMAP;
begin
if FHandle <> 0 then
begin
if FOldBitmap <> 0 then SelectObject(FHandle, FOldBitmap);
if FOldPalette <> 0 then SelectPalette(FHandle, FOldPalette, True);
H := FHandle;
Handle := 0;
DeleteDC(H);
BitmapCanvasList.Remove(Self);
end;
end;
procedure TBitmapCanvas.CreateHandle;
var
H: HBITMAP;
begin
if FBitmap <> nil then
begin
FBitmap.HandleNeeded;
DeselectBitmap(FBitmap.FImage.FHandle);
H := CreateCompatibleDC(0);
if FBitmap.FImage.FHandle <> 0 then
FOldBitmap := SelectObject(H, FBitmap.FImage.FHandle) else
FOldBitmap := 0;
if FBitmap.FImage.FPalette <> 0 then
begin
FOldPalette := SelectPalette(H, FBitmap.FImage.FPalette, True);
RealizePalette(H);
end
else
FOldPalette := 0;
Handle := H;
BitmapCanvasList.Add(Self);
end;
end;
{ TInternalImage }
procedure TInternalImage.Reference;
begin
Inc(FRefCount);
end;
procedure TInternalImage.Release;
begin
if Pointer(Self) <> nil then
begin
Dec(FRefCount);
if FRefCount = 0 then
begin
FMemoryImage.Free;
FreeHandle;
Free;
end;
end;
end;
{ TBitmapImage }
procedure TBitmapImage.FreeHandle;
begin
if FHandle <> 0 then
begin
DeselectBitmap(FHandle);
DeleteObject(FHandle);
end;
if FPalette <> 0 then DeleteObject(FPalette);
FHandle := 0;
FPalette := 0;
end;
{ TBitmap }
function CopyBitmap(Handle: HBITMAP; Palette: HPALETTE; NewWidth,
NewHeight: Integer; Canvas: TCanvas; Monochrome: Boolean): HBITMAP;
var
OldScr, NewScr: HBITMAP;
ScreenDC, NewImageDC, OldImageDC: HDC;
begin
Result := 0;
if (Handle = 0) and ((NewWidth = 0) or (NewHeight = 0)) then Exit;
ScreenDC := GetDC(0);
NewImageDC := CreateCompatibleDC(ScreenDC);
try
if Monochrome then
Result := CreateBitmap(NewWidth, NewHeight, 1, 1, nil)
else
Result := CreateCompatibleBitmap(ScreenDC, NewWidth, NewHeight);
if Result = 0 then OutOfResources;
NewScr := SelectObject(NewImageDC, Result);
try
if Canvas <> nil then
begin
FillRect(NewImageDC, Rect(0, 0, NewWidth, NewHeight),
Canvas.Brush.Handle);
SetTextColor(NewImageDC, ColorToRGB(Canvas.Font.Color));
SetBkColor(NewImageDC, ColorToRGB(Canvas.Brush.Color));
end
else
PatBlt(NewImageDC, 0, 0, NewWidth, NewHeight, WHITENESS);
if Handle <> 0 then
begin
OldImageDC := CreateCompatibleDC(ScreenDC);
if OldImageDC = 0 then OutOfResources;
try
DeselectBitmap(Handle);
OldScr := SelectObject(OldImageDC, Handle);
if Palette <> 0 then
begin
SelectPalette(OldImageDC, Palette, True);
RealizePalette(OldImageDC);
SelectPalette(NewImageDC, Palette, True);
RealizePalette(NewImageDC);
end;
if Canvas <> nil then
begin
SetTextColor(OldImageDC, ColorToRGB(Canvas.Font.Color));
SetBkColor(OldImageDC, ColorToRGB(Canvas.Brush.Color));
end;
BitBlt(NewImageDC, 0, 0, NewWidth, NewHeight, OldImageDC, 0, 0, SRCCOPY);
SelectObject(OldImageDC, OldScr);
finally
DeleteDC(OldImageDC);
end;
end;
except
SelectObject(NewImageDC, NewScr);
DeleteObject(Result);
raise;
end;
finally
DeleteDC(NewImageDC);
ReleaseDC(0, ScreenDC);
end;
end;
function CopyPalette(Palette: HPALETTE): HPALETTE;
var
PaletteSize: Integer;
LogSize: Integer;
LogPalette: PLogPalette;
begin
Result := 0;
if Palette = 0 then Exit;
PaletteSize := 0;
if GetObject(Palette, SizeOf(PaletteSize), @PaletteSize) = 0 then Exit;
if PaletteSize = 0 then Exit;
LogSize := SizeOf(TLogPalette) + (PaletteSize - 1) * SizeOf(TPaletteEntry);
GetMem(LogPalette, LogSize);
try
with LogPalette^ do
begin
palVersion := $0300;
palNumEntries := PaletteSize;
GetPaletteEntries(Palette, 0, PaletteSize, palPalEntry);
end;
Result := CreatePalette(LogPalette^);
finally
FreeMem(LogPalette, LogSize);
end;
end;
constructor TBitmap.Create;
begin
inherited Create;
FImage := TBitmapImage.Create;
FImage.Reference;
end;
destructor TBitmap.Destroy;
begin
FImage.Release;
FCanvas.Free;
inherited Destroy;
end;
procedure TBitmap.Assign(Source: TPersistent);
begin
if (Source = nil) or (Source is TBitmap) then
begin
if Source <> nil then
begin
TBitmap(Source).FImage.Reference;
FImage.Release;
FImage := TBitmap(Source).FImage;
end else
NewImage(0, 0, 0, 0, False, nil, dtNone, nil, nil);
Changed(Self);
Exit;
end;
inherited Assign(Source);
end;
procedure TBitmap.CopyImage(AHandle: HBITMAP; APalette: HPALETTE;
AWidth, AHeight: Integer; AMonochrome: Boolean);
begin
FreeContext;
AHandle := CopyBitmap(AHandle, APalette, AWidth, AHeight, FCanvas, AMonochrome);
try
APalette := CopyPalette(APalette);
try
NewImage(AHandle, APalette, AWidth, AHeight, AMonochrome, nil, dtNone, nil, nil);
except
DeleteObject(APalette);
raise;
end;
except
DeleteObject(AHandle);
raise;
end;
end;
{ Called by the FCanvas whenever an operation is going to be performed on the
bitmap that would modify it. Since modifications should only affect this
TBitmap, the handle needs to be 'cloned' if it is being refered to by more
than one TBitmap }
procedure TBitmap.Changing(Sender: TObject);
begin
FreeImage;
end;
procedure TBitmap.Dormant;
begin
MemoryImageNeeded;
FImage.FreeHandle;
end;
procedure TBitmap.Draw(ACanvas: TCanvas; const Rect: TRect);
var
OldPalette: HPalette;
UseHandle: Boolean;
RestorePalette: Boolean;
begin
if not Monochrome then SetStretchBltMode(ACanvas.Handle, STRETCH_DELETESCANS);
UseHandle := (Assigned(FCanvas) and (FImage.FHandle <> 0)) or
((GetDeviceCaps(ACanvas.Handle, RASTERCAPS) and RC_STRETCHDIB) = 0) or
(FImage.FDIBType <> dtWin) or (FImage.FMemoryImage = nil) or
(FImage.FMemoryImage.Size = 0);
with Rect, FImage do
begin
ACanvas.RequiredState(csAllValid);
PaletteNeeded;
OldPalette := 0;
RestorePalette := False;
if FPalette <> 0 then
begin
OldPalette := SelectPalette(ACanvas.FHandle, FPalette, True);
RealizePalette(ACanvas.FHandle);
RestorePalette := True;
end;
try
if UseHandle then
begin
Canvas.RequiredState(csAllValid);
StretchBlt(ACanvas.Handle, Left, Top, Right - Left, Bottom - Top,
Canvas.FHandle, 0, 0, FWidth, FHeight, ACanvas.CopyMode);
end
else { Draw without requiring a bitmap handle and memory dc }
StretchDIBits(ACanvas.FHandle, Left, Top, Right-Left, Bottom - Top,
0, 0, FWidth, FHeight, FDIBBits, PBitmapInfo(FDIBHeader)^,
DIB_RGB_COLORS, ACanvas.CopyMode);
finally
if RestorePalette then
SelectPalette(ACanvas.FHandle, OldPalette, True);
end;
end;
end;
procedure TBitmap.FreeImage;
begin
with FImage do
if FRefCount > 1 then
CopyImage(FHandle, FPalette, FWidth, FHeight, FMonochrome)
else
begin
FMemoryImage.Free;
FMemoryImage := nil;
FDIBHeader := nil;
FDIBBits := nil;
FDIBType := dtNone;
end;
end;
function TBitmap.GetEmpty;
begin
with FImage do
Result := (FHandle = 0) and (FMemoryImage = nil);
end;
function TBitmap.GetCanvas: TCanvas;
begin
if FCanvas = nil then
begin
HandleNeeded;
FCanvas := TBitmapCanvas.Create(Self);
FCanvas.OnChange := Changed;
FCanvas.OnChanging := Changing;
end;
Result := FCanvas;
end;
{ Since the user might modify the contents of the HBITMAP it must not be
shared by another TBitmap when given to the user nor should it be selected
into a DC. }
function TBitmap.GetHandle: HBITMAP;
begin
FreeContext;
HandleNeeded;
Changing(Self);
Result := FImage.FHandle;
end;
function TBitmap.GetHeight: Integer;
begin
Result := FImage.FHeight;
end;
function TBitmap.GetMonochrome: Boolean;
begin
Result := FImage.FMonochrome;
end;
function TBitmap.GetPalette: HPALETTE;
begin
PaletteNeeded;
Result := FImage.FPalette;
end;
function TBitmap.GetTransparentColor: TColor;
begin
if Monochrome then
Result := clWhite else
Result := Canvas.Pixels[0, Height - 1];
Result := Result or $02000000;
end;
function TBitmap.GetWidth: Integer;
begin
Result := FImage.FWidth;
end;
procedure TBitmap.FreeContext;
begin
if (FCanvas <> nil) then TBitmapCanvas(FCanvas).FreeContext;
end;
procedure TBitmap.HandleNeeded;
begin
with FImage do
begin
if FHandle <> 0 then Exit;
if FMemoryImage = nil then Exit;
FMemoryImage.Position := 0;
ReadDIB(FMemoryImage, FHandle, FPalette, FMemoryImage.Size);
end;
end;
procedure TBitmap.MemoryImageNeeded;
var
Image: TMemoryStream;
Header, Bits: Pointer;
begin
with FImage do
begin
if FMemoryImage = nil then
begin
Image := TMemoryStream.Create;
try
if FHandle <> 0 then
DIBFromBit(Image, FHandle, FPalette, 0, Header, Bits);
Image.Position := 0;
except
Image.Free;
raise;
end;
FMemoryImage := Image;
FDIBHeader := Header;
FDIBBits := Bits;
case PLongint(FDIBHeader)^ of
sizeof(TBitmapInfoHeader): FDIBType := dtWin;
sizeof(TBitmapCoreHeader): FDIBType := dtPM;
else
FDIBType := dtNone;
end;
end;
end;
end;
procedure TBitmap.PaletteNeeded;
begin
if FIgnorePalette then Exit;
with FImage do
if FPalette = 0 then
case FDIBType of
dtWin: FPalette := PaletteFromW3DIB(PBitmapInfo(FDIBHeader)^);
dtPM: FPalette := PaletteFromPM1DIB(PBitmapCoreInfo(FDIBHeader)^);
end;
end;
procedure TBitmap.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
var
ABitmap: HBITMAP;
BitmapInfo: Windows.TBitmap;
begin
if (AFormat <> CF_BITMAP) or (AData = 0) then
InvalidGraphic(SUnknownClipboardFormat);
FreeContext;
GetObject(AData, SizeOf(BitmapInfo), @BitmapInfo);
ABitmap := CopyBitmap(AData, APalette, BitmapInfo.bmWidth, BitmapInfo.bmHeight,
nil, (BitmapInfo.bmPlanes = 1) and (BitmapInfo.bmBitsPixel = 1));
try
APalette := CopyPalette(APalette);
try
NewImage(ABitmap, APalette, BitmapInfo.bmWidth, BitmapInfo.bmHeight,
(BitmapInfo.bmPlanes = 1) and (BitmapInfo.bmBitsPixel = 1), nil,
dtNone, nil, nil);
except
DeleteObject(APalette);
raise;
end;
except
DeleteObject(ABitmap);
raise;
end;
Changed(Self);
end;
procedure TBitmap.LoadFromStream(Stream: TStream);
begin
ReadStream(Stream.Size - Stream.Position, Stream);
Changed(Self);
end;
procedure TBitmap.LoadFromResourceName(Instance: THandle; const ResName: string);
var
Stream: TCustomMemoryStream;
begin
Stream := TResourceStream.Create(Instance, ResName, RT_BITMAP);
try
ReadStreamDIB(Stream);
except
Stream.Free;
raise;
end;
Changed(Self);
end;
procedure TBitmap.LoadFromResourceID(Instance: THandle; ResID: Integer);
var
Stream: TCustomMemoryStream;
begin
Stream := TResourceStream.CreateFromID(Instance, ResID, RT_BITMAP);
try
ReadStreamDIB(Stream);
except
Stream.Free;
raise;
end;
Changed(Self);
end;
procedure TBitmap.NewImage(NewHandle: HBITMAP; NewPalette: HPALETTE;
NewWidth, NewHeight: Integer; NewMonochrome: Boolean; NewImage: TCustomMemoryStream;
NewDIBType: TDIBType; NewDIBHeader, NewDIBBits: Pointer);
var
Image: TBitmapImage;
begin
Image := TBitmapImage.Create;
with Image do
try
FHandle := NewHandle;
FPalette := NewPalette;
FWidth := NewWidth;
FHeight := NewHeight;
FMonochrome := NewMonochrome;
FMemoryImage := NewImage;
FDIBType := NewDIBType;
FDIBHeader := NewDIBHeader;
FDIBBits := NewDIBBits;
except
Image.Free;
raise;
end;
FImage.Release;
FImage := Image;
FImage.Reference;
end;
procedure TBitmap.ReadData(Stream: TStream);
var
Size: Longint;
begin
Stream.Read(Size, SizeOf(Size));
ReadStream(Size, Stream);
Changed(Self);
end;
procedure TBitmap.ReadStream(Size: Longint; Stream: TStream);
var
Bmf: TBitmapFileHeader;
Image: TMemoryStream;
begin
FreeContext;
if Size = 0 then
NewImage(0, 0, 0, 0, False, nil, dtNone, nil, nil)
else
begin
Stream.ReadBuffer(Bmf, SizeOf(Bmf));
if Bmf.bfType <> $4D42 then InvalidBitmap;
Image := TMemoryStream.Create;
try
Image.SetSize(Size - sizeof(BMF));
Stream.ReadBuffer(Image.Memory^, Size - sizeof(BMF));
ReadStreamDIB(Image);
except
Image.Free;
raise;
end;
end;
end;
procedure TBitmap.ReadStreamDIB(Image: TCustomMemoryStream);
var
BC: TBitmapCoreHeader;
BI: TBitmapInfoHeader;
IWidth, IHeight: Integer;
IMonochrome: Boolean;
IDIBType: TDIBType;
IDIBHeader, IDIBBits: Pointer;
Size: Integer;
begin
IDIBHeader := Image.Memory;
Image.Read(Size, SizeOf(Size));
Image.Seek(-SizeOf(Size), 1);
if Size = SizeOf(BC) then
begin
Image.Read(BC, SizeOf(BC));
IHeight := BC.bcHeight;
IWidth := BC.bcWidth;
IMonochrome := (BC.bcPlanes = 1) and (BC.bcBitCount = 1);
IDIBType := dtPM;
IDIBBits := Pointer(Longint(IDIBHeader) + Sizeof(BC) +
GetDInColors(BC.bcBitCount) * SizeOf(TRGBTriple));
end
else if Size = SizeOf(BI) then
begin
Image.Read(BI, SizeOf(BI));
IHeight := BI.biHeight;
IWidth := BI.biWidth;
IMonochrome := (BI.biPlanes = 1) and (BI.biBitCount = 1);
IDIBType := dtWin;
if BI.biClrUsed = 0 then
BI.biClrUsed := GetDInColors(BI.biBitCount);
IDIBBits := Pointer(Longint(IDIBHeader) + sizeof(BI) +
BI.biClrUsed * SizeOf(TRgbQuad));
end
else InvalidBitmap;
Image.Position := 0;
NewImage(0, 0, IWidth, IHeight, IMonochrome, Image, IDIBType,
IDIBHeader, IDIBBits);
end;
procedure TBitmap.SetHandle(Value: HBITMAP);
var
BitmapInfo: Windows.TBitmap;
APalette: HPALETTE;
begin
with FImage do
if FHandle <> Value then
begin
FreeContext;
if Value <> 0 then
GetObject(Value, SizeOf(BitmapInfo), @BitmapInfo) else
FillChar(BitmapInfo, SizeOf(BitmapInfo), 0);
if FRefCount = 1 then
begin
APalette := FPalette;
FPalette := 0;
end
else
APalette := CopyPalette(FPalette);
try
NewImage(Value, APalette, BitmapInfo.bmWidth, BitmapInfo.bmHeight,
(BitmapInfo.bmPlanes = 1) and (BitmapInfo.bmBitsPixel = 1), nil,
dtNone, nil, nil);
except
DeleteObject(APalette);
raise;
end;
Changed(Self);
end;
end;
procedure TBitmap.SetPalette(Value: HPALETTE);
var
AHandle: HBITMAP;
begin
with FImage do
if FPalette <> Value then
begin
FreeContext;
HandleNeeded;
if FRefCount = 1 then
begin
AHandle := FHandle;
FHandle := 0;
end
else
AHandle := CopyBitmap(FHandle, FPalette, FWidth, FHeight, nil, FMonochrome);
try
NewImage(AHandle, Value, FWidth, FHeight, FMonochrome, nil, dtNone, nil, nil);
except
DeleteObject(AHandle);
raise;
end;
Changed(Self);
end;
end;
procedure TBitmap.SetHeight(Value: Integer);
begin
with FImage do
if FHeight <> Value then
begin
CopyImage(FHandle, FPalette, FWidth, Value, FMonochrome);
Changed(Self);
end;
end;
procedure TBitmap.SetMonochrome(Value: Boolean);
begin
with FImage do
if Value <> FMonochrome then
begin
CopyImage(FHandle, FPalette, FWidth, FHeight, Value);
Changed(Self);
end;
end;
procedure TBitmap.SetWidth(Value: Integer);
begin
with FImage do
if FWidth <> Value then
begin
CopyImage(FHandle, FPalette, Value, FHeight, FMonochrome);
Changed(Self);
end;
end;
procedure TBitmap.WriteData(Stream: TStream);
begin
WriteStream(Stream, True);
end;
procedure TBitmap.WriteStream(Stream: TStream; WriteSize: Boolean);
var
Size: Longint;
BMF: TBitmapFileHeader;
begin
with FImage do
begin
MemoryImageNeeded;
Size := FMemoryImage.Size;
if Size <> 0 then Inc(Size, sizeof(BMF));
if WriteSize then Stream.WriteBuffer(Size, SizeOf(Size));
if Size <> 0 then
begin
FillChar(BMF, sizeof(BMF), 0);
BMF.bfType := $4D42;
BMF.bfSize := Size;
BMF.bfOffBits := Longint(FDIBBits) - Longint(FDIBHeader) + sizeof(BMF);
Stream.WriteBuffer(BMF, Sizeof(BMF));
Stream.WriteBuffer(FMemoryImage.Memory^, FMemoryImage.Size);
end;
end;
end;
function TBitmap.ReleaseHandle: HBITMAP;
begin
HandleNeeded;
Changing(Self);
Result := FImage.FHandle;
FImage.FHandle := 0;
end;
function TBitmap.ReleasePalette: HPALETTE;
begin
HandleNeeded;
Changing(Self);
Result := FImage.FPalette;
FImage.FPalette := 0;
end;
procedure TBitmap.SaveToStream(Stream: TStream);
begin
WriteStream(Stream, False);
end;
procedure TBitmap.SaveToClipboardFormat(var Format: Word; var Data: THandle;
var APalette: HPALETTE);
begin
Format := CF_BITMAP;
HandleNeeded;
with FImage do
Data := CopyBitmap(FHandle, FPalette, FWidth, FHeight, FCanvas, FMonochrome);
try
APalette := CopyPalette(FImage.FPalette);
except
DeleteObject(Data);
raise;
end;
end;
{ TIconImage }
procedure TIconImage.FreeHandle;
begin
if FHandle <> 0 then DestroyIcon(FHandle);
FHandle := 0;
end;
{ TIcon }
constructor TIcon.Create;
begin
inherited Create;
FImage := TIconImage.Create;
FImage.Reference;
end;
destructor TIcon.Destroy;
begin
FImage.Release;
inherited Destroy;
end;
procedure TIcon.Assign(Source: TPersistent);
begin
if (Source = nil) or (Source is TIcon) then
begin
if Source <> nil then
begin
TIcon(Source).FImage.Reference;
FImage.Release;
FImage := TIcon(Source).FImage;
end else
NewImage(0, nil);
Changed(Self);
Exit;
end;
inherited Assign(Source);
end;
procedure TIcon.Draw(ACanvas: TCanvas; const Rect: TRect);
begin
with Rect.TopLeft do
begin
ACanvas.RequiredState([csHandleValid]);
DrawIcon(ACanvas.FHandle, X, Y, Handle);
end;
end;
function TIcon.GetEmpty: Boolean;
begin
with FImage do
Result := (FHandle = 0) and (FMemoryImage = nil);
end;
function TIcon.GetHandle: HICON;
begin
HandleNeeded;
Result := FImage.FHandle;
end;
function TIcon.GetHeight: Integer;
begin
Result := GetSystemMetrics(SM_CYICON);
end;
function TIcon.GetWidth: Integer;
begin
Result := GetSystemMetrics(SM_CXICON);
end;
procedure TIcon.HandleNeeded;
var
CI: TCursorOrIcon;
NewHandle: HICON;
begin
with FImage do
begin
if FHandle <> 0 then Exit;
if FMemoryImage = nil then Exit;
FMemoryImage.Position := 0;
FMemoryImage.ReadBuffer(CI, SizeOf(CI));
case CI.wType of
RC3_STOCKICON: NewHandle := StockIcon;
RC3_ICON: ReadIcon(FMemoryImage, NewHandle, CI.Count, SizeOf(CI));
else
InvalidIcon;
end;
FHandle := NewHandle;
end;
end;
procedure TIcon.ImageNeeded;
var
Image: TMemoryStream;
CI: TCursorOrIcon;
begin
with FImage do
begin
if FMemoryImage <> nil then Exit;
if FHandle = 0 then InvalidIcon;
Image := TMemoryStream.Create;
try
if GetHandle = StockIcon then
begin
FillChar(CI, SizeOf(CI), 0);
Image.WriteBuffer(CI, SizeOf(CI));
end
else
WriteIcon(Image, Handle, False);
except
Image.Free;
raise;
end;
FMemoryImage := Image;
end;
end;
procedure TIcon.LoadFromStream(Stream: TStream);
var
Image: TMemoryStream;
CI: TCursorOrIcon;
begin
Image := TMemoryStream.Create;
try
Image.SetSize(Stream.Size - Stream.Position);
Stream.ReadBuffer(Image.Memory^, Image.Size);
Image.ReadBuffer(CI, SizeOf(CI));
if not (CI.wType in [RC3_STOCKICON, RC3_ICON]) then InvalidIcon;
NewImage(0, Image);
except
Image.Free;
raise;
end;
Changed(Self);
end;
procedure TIcon.NewImage(NewHandle: HICON; NewImage: TMemoryStream);
var
Image: TIconImage;
begin
Image := TIconImage.Create;
try
Image.FHandle := NewHandle;
Image.FMemoryImage := NewImage;
except
Image.Free;
raise;
end;
Image.Reference;
FImage.Release;
FImage := Image;
end;
function TIcon.ReleaseHandle: HICON;
begin
with FImage do
begin
if FRefCount > 1 then NewImage(CopyIcon(FHandle), nil);
Result := FHandle;
FHandle := 0;
end;
Changed(Self);
end;
procedure TIcon.SetHandle(Value: HICON);
begin
NewImage(Value, nil);
Changed(Self);
end;
procedure TIcon.SetHeight(Value: Integer);
begin
InvalidOperation(SChangeIconSize);
end;
procedure TIcon.SetWidth(Value: Integer);
begin
InvalidOperation(SChangeIconSize);
end;
procedure TIcon.SaveToStream(Stream: TStream);
begin
ImageNeeded;
with FImage.FMemoryImage do Stream.WriteBuffer(Memory^, Size);
end;
procedure TIcon.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
begin
InvalidOperation(SIconToClipboard);
end;
procedure TIcon.SaveToClipboardFormat(var Format: Word; var Data: THandle;
var APalette: HPALETTE);
begin
InvalidOperation(SIconToClipboard);
end;
function GraphicFilter(GraphicClass: TGraphicClass): string;
var
Graphic: PFileFormat;
Count: Integer;
Filters: string;
begin
Result := '';
Filters := '';
Count := 0;
Graphic := FileFormatList;
while Graphic <> nil do
begin
if Graphic^.GraphicClass.InheritsFrom(GraphicClass) then
with Graphic^ do
begin
if Count <> 0 then
begin
Result := Result + '|';
Filters := Filters + ';';
end;
if (Description = '') and (DescResID <> 0) then
Description := LoadStr(DescResID);
FmtStr(Result, '%s%s (*.%s)|*.%2:s', [Result, Description, Extension]);
FmtStr(Filters, '%s*.%s', [Filters, Extension]);
Inc(Count);
end;
Graphic := Graphic^.Next;
end;
if Count > 1 then
FmtStr(Result, '%s (%s)|%1:s|%s', [LoadStr(sAllFilter), Filters, Result]);
end;
function GraphicExtension(GraphicClass: TGraphicClass): string;
var
Graphic: PFileFormat;
begin
Result := '';
Graphic := FileFormatList;
while Graphic <> nil do
if Graphic^.GraphicClass.InheritsFrom(GraphicClass) then
begin
Result := Graphic^.Extension;
Exit;
end
else Graphic := Graphic^.Next;
end;
function GetDefFontCharSet: TFontCharSet;
var
DisplayDC: HDC;
TxtMetric: TTEXTMETRIC;
begin
Result := DEFAULT_CHARSET;
DisplayDC := GetDC(0);
if (DisplayDC <> 0) then
begin
if (SelectObject(DisplayDC, StockFont) <> 0) then
if (GetTextMetrics(DisplayDC, TxtMetric)) then
Result := TxtMetric.tmCharSet;
ReleaseDC(0, DisplayDC);
end;
end;
procedure InitDefFontData;
var
Charset: TFontCharset;
begin
DefFontData.Height := -MulDiv(8, ScreenLogPixels, 72);
if not SysLocale.FarEast then Exit;
Charset := GetDefFontCharset;
case Charset of
SHIFTJIS_CHARSET:
begin
DefFontData.Name := 'élér éoâSâVâbâN';
DefFontData.Height := -MulDiv(9, ScreenLogPixels, 72);
DefFontData.CharSet := CharSet;
end;
end;
end;
procedure InitGraphics;
var
DC: HDC;
begin
DC := GetDC(0);
ScreenLogPixels := GetDeviceCaps(DC, LOGPIXELSY);
ReleaseDC(0,DC);
DefFontData.Height := -MulDiv(8, ScreenLogPixels, 72);
StockPen := GetStockObject(BLACK_PEN);
StockBrush := GetStockObject(HOLLOW_BRUSH);
StockFont := GetStockObject(SYSTEM_FONT);
InitDefFontData;
StockIcon := LoadIcon(0, IDI_APPLICATION);
FontManager := TResourceManager.Create(SizeOf(TFontData));
PenManager := TResourceManager.Create(SizeOf(TPenData));
BrushManager := TResourceManager.Create(SizeOf(TBrushData));
BitmapCanvasList := TList.Create;
CanvasList := TList.Create;
RegisterIntegerConsts(TypeInfo(TColor), IdentToColor, ColorToIdent);
RegisterIntegerConsts(TypeInfo(TFontCharset), IdentToCharset, CharsetToIdent);
end;
end.