mirror of
http://gitlab.expertsoft.com.ua/git/expertcad
synced 2026-01-11 22:45:39 +02:00
7021 lines
196 KiB
ObjectPascal
7021 lines
196 KiB
ObjectPascal
|
||
{*******************************************************}
|
||
{ }
|
||
{ Borland Delphi Visual Component Library }
|
||
{ }
|
||
{ Copyright (c) 1995-2001 Borland Software Corporation }
|
||
{ }
|
||
{*******************************************************}
|
||
|
||
unit Graphics;
|
||
|
||
{$P+,S-,W-,R-,T-,X+,H+,B-}
|
||
{$C PRELOAD}
|
||
|
||
interface
|
||
|
||
{$IFDEF MSWINDOWS}
|
||
uses Windows,
|
||
{$ENDIF}
|
||
{$IFDEF LINUX}
|
||
uses WinUtils, Libc, Windows,
|
||
{$ENDIF}
|
||
SysUtils, Classes;
|
||
|
||
{ Graphics Objects }
|
||
|
||
type
|
||
PColor = ^TColor;
|
||
TColor = -$7FFFFFFF-1..$7FFFFFFF;
|
||
{$NODEFINE TColor}
|
||
|
||
(*$HPPEMIT 'namespace Graphics'*)
|
||
(*$HPPEMIT '{'*)
|
||
(*$HPPEMIT ' enum TColor {clMin=-0x7fffffff-1, clMax=0x7fffffff};'*)
|
||
(*$HPPEMIT '}'*)
|
||
|
||
|
||
const
|
||
clSystemColor = $FF000000;
|
||
|
||
clScrollBar = TColor(clSystemColor or COLOR_SCROLLBAR);
|
||
clBackground = TColor(clSystemColor or COLOR_BACKGROUND);
|
||
clActiveCaption = TColor(clSystemColor or COLOR_ACTIVECAPTION);
|
||
clInactiveCaption = TColor(clSystemColor or COLOR_INACTIVECAPTION);
|
||
clMenu = TColor(clSystemColor or COLOR_MENU);
|
||
clWindow = TColor(clSystemColor or COLOR_WINDOW);
|
||
clWindowFrame = TColor(clSystemColor or COLOR_WINDOWFRAME);
|
||
clMenuText = TColor(clSystemColor or COLOR_MENUTEXT);
|
||
clWindowText = TColor(clSystemColor or COLOR_WINDOWTEXT);
|
||
clCaptionText = TColor(clSystemColor or COLOR_CAPTIONTEXT);
|
||
clActiveBorder = TColor(clSystemColor or COLOR_ACTIVEBORDER);
|
||
clInactiveBorder = TColor(clSystemColor or COLOR_INACTIVEBORDER);
|
||
clAppWorkSpace = TColor(clSystemColor or COLOR_APPWORKSPACE);
|
||
clHighlight = TColor(clSystemColor or COLOR_HIGHLIGHT);
|
||
clHighlightText = TColor(clSystemColor or COLOR_HIGHLIGHTTEXT);
|
||
clBtnFace = TColor(clSystemColor or COLOR_BTNFACE);
|
||
clBtnShadow = TColor(clSystemColor or COLOR_BTNSHADOW);
|
||
clGrayText = TColor(clSystemColor or COLOR_GRAYTEXT);
|
||
clBtnText = TColor(clSystemColor or COLOR_BTNTEXT);
|
||
clInactiveCaptionText = TColor(clSystemColor or COLOR_INACTIVECAPTIONTEXT);
|
||
clBtnHighlight = TColor(clSystemColor or COLOR_BTNHIGHLIGHT);
|
||
cl3DDkShadow = TColor(clSystemColor or COLOR_3DDKSHADOW);
|
||
cl3DLight = TColor(clSystemColor or COLOR_3DLIGHT);
|
||
clInfoText = TColor(clSystemColor or COLOR_INFOTEXT);
|
||
clInfoBk = TColor(clSystemColor or COLOR_INFOBK);
|
||
clHotLight = TColor(clSystemColor or COLOR_HOTLIGHT);
|
||
clGradientActiveCaption = TColor(clSystemColor or COLOR_GRADIENTACTIVECAPTION);
|
||
clGradientInactiveCaption = TColor(clSystemColor or COLOR_GRADIENTINACTIVECAPTION);
|
||
clMenuHighlight = TColor(clSystemColor or COLOR_MENUHILIGHT);
|
||
clMenuBar = TColor(clSystemColor or COLOR_MENUBAR);
|
||
|
||
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);
|
||
StandardColorsCount = 16;
|
||
|
||
clMoneyGreen = TColor($C0DCC0);
|
||
clSkyBlue = TColor($F0CAA6);
|
||
clCream = TColor($F0FBFF);
|
||
clMedGray = TColor($A4A0A0);
|
||
ExtendedColorsCount = 4;
|
||
|
||
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;
|
||
|
||
{ Icon and cursor types }
|
||
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;
|
||
|
||
{$EXTERNALSYM HMETAFILE}
|
||
HMETAFILE = THandle;
|
||
{$EXTERNALSYM HENHMETAFILE}
|
||
HENHMETAFILE = THandle;
|
||
|
||
EInvalidGraphic = class(Exception);
|
||
EInvalidGraphicOperation = class(Exception);
|
||
|
||
TGraphic = class;
|
||
TBitmap = class;
|
||
TIcon = class;
|
||
TMetafile = class;
|
||
|
||
TResData = record
|
||
Handle: THandle;
|
||
end;
|
||
|
||
TFontPitch = (fpDefault, fpVariable, fpFixed);
|
||
TFontName = type string;
|
||
TFontCharset = 0..255;
|
||
|
||
{ Changes to the following types should be reflected in the $HPPEMIT directives. }
|
||
|
||
TFontDataName = string[LF_FACESIZE - 1];
|
||
{$NODEFINE TFontDataName}
|
||
TFontStyle = (fsBold, fsItalic, fsUnderline, fsStrikeOut);
|
||
{$NODEFINE TFontStyle}
|
||
TFontStyles = set of TFontStyle;
|
||
TFontStylesBase = set of TFontStyle;
|
||
{$NODEFINE TFontStylesBase}
|
||
|
||
(*$HPPEMIT 'namespace Graphics'*)
|
||
(*$HPPEMIT '{'*)
|
||
(*$HPPEMIT ' enum TFontStyle { fsBold, fsItalic, fsUnderline, fsStrikeOut };'*)
|
||
(*$HPPEMIT ' typedef SmallStringBase<31> TFontDataName;'*)
|
||
(*$HPPEMIT ' typedef SetBase<TFontStyle, fsBold, fsStrikeOut> TFontStylesBase;'*)
|
||
(*$HPPEMIT '}'*)
|
||
|
||
TFontData = record
|
||
Handle: HFont;
|
||
Height: Integer;
|
||
Pitch: TFontPitch;
|
||
Style: TFontStylesBase;
|
||
Charset: TFontCharset;
|
||
Name: TFontDataName;
|
||
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;
|
||
FOwnerLock: PRTLCriticalSection;
|
||
protected
|
||
procedure Changed; dynamic;
|
||
procedure Lock;
|
||
procedure Unlock;
|
||
public
|
||
function HandleAllocated: Boolean;
|
||
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
||
property OwnerCriticalSection: PRTLCriticalSection read FOwnerLock write FOwnerLock;
|
||
end;
|
||
|
||
IChangeNotifier = interface
|
||
['{1FB62321-44A7-11D0-9E93-0020AF3D82DA}']
|
||
procedure Changed;
|
||
end;
|
||
|
||
TFont = class(TGraphicsObject)
|
||
private
|
||
FColor: TColor;
|
||
FPixelsPerInch: Integer;
|
||
FNotify: IChangeNotifier;
|
||
procedure GetData(var FontData: TFontData);
|
||
procedure SetData(const FontData: TFontData);
|
||
protected
|
||
procedure Changed; override;
|
||
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 FontAdapter: IChangeNotifier read FNotify write FNotify;
|
||
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;
|
||
|
||
TExtPen = class(TPen)
|
||
private
|
||
FGeometric:Boolean;
|
||
function GetHandle: HPen;
|
||
procedure SetHandle(Value: HPen);
|
||
procedure SetGeometric(const Value: Boolean);
|
||
public
|
||
constructor Create;
|
||
property Handle: HPen read GetHandle write SetHandle;
|
||
Property Geometric:Boolean read FGeometric write SetGeometric;
|
||
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;
|
||
|
||
TFontRecall = class(TRecall)
|
||
public
|
||
constructor Create(AFont: TFont);
|
||
end;
|
||
|
||
TPenRecall = class(TRecall)
|
||
public
|
||
constructor Create(APen: TPen);
|
||
end;
|
||
|
||
TBrushRecall = class(TRecall)
|
||
public
|
||
constructor Create(ABrush: TBrush);
|
||
end;
|
||
|
||
TFillStyle = (fsSurface, fsBorder);
|
||
TFillMode = (fmAlternate, fmWinding);
|
||
|
||
TCopyMode = Longint;
|
||
|
||
TCanvasStates = (csHandleValid, csFontValid, csPenValid, csBrushValid);
|
||
TCanvasState = set of TCanvasStates;
|
||
TCanvasOrientation = (coLeftToRight, coRightToLeft);
|
||
|
||
TCanvas = class(TPersistent)
|
||
private
|
||
FHandle: HDC;
|
||
State: TCanvasState;
|
||
FFont: TFont;
|
||
FPen: TPen;
|
||
FBrush: TBrush;
|
||
FPenPos: TPoint;
|
||
FCopyMode: TCopyMode;
|
||
FOnChange: TNotifyEvent;
|
||
FOnChanging: TNotifyEvent;
|
||
FLock: TRTLCriticalSection;
|
||
FLockCount: Integer;
|
||
FTextFlags: Longint;
|
||
|
||
procedure CreateBrush;
|
||
procedure CreateFont;
|
||
procedure CreatePen;
|
||
procedure BrushChanged(ABrush: TObject);
|
||
procedure DeselectHandles;
|
||
function GetCanvasOrientation: TCanvasOrientation;
|
||
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); overload;
|
||
procedure Ellipse(const Rect: TRect); overload;
|
||
procedure FillRect(const Rect: TRect);
|
||
procedure FloodFill(X, Y: Integer; Color: TColor; FillStyle: TFillStyle);
|
||
procedure FrameRect(const Rect: TRect);
|
||
function HandleAllocated: Boolean;
|
||
procedure LineTo(X, Y: Integer);
|
||
procedure Lock;
|
||
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 PolyBezier(const Points: array of TPoint);
|
||
procedure PolyBezierTo(const Points: array of TPoint);
|
||
procedure Rectangle(X1, Y1, X2, Y2: Integer); overload;
|
||
procedure Rectangle(const Rect: TRect); overload;
|
||
procedure Refresh;
|
||
procedure RoundRect(X1, Y1, X2, Y2, X3, Y3: Integer);
|
||
procedure StretchDraw(const Rect: TRect; Graphic: TGraphic);
|
||
function TextExtent(const Text: string): TSize;
|
||
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;
|
||
function TryLock: Boolean;
|
||
procedure Unlock;
|
||
property ClipRect: TRect read GetClipRect;
|
||
property Handle: HDC read GetHandle write SetHandle;
|
||
property LockCount: Integer read FLockCount;
|
||
property CanvasOrientation: TCanvasOrientation read GetCanvasOrientation;
|
||
property PenPos: TPoint read GetPenPos write SetPenPos;
|
||
property Pixels[X, Y: Integer]: TColor read GetPixel write SetPixel;
|
||
property TextFlags: Longint read FTextFlags write FTextFlags;
|
||
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;
|
||
|
||
{ TProgressEvent is a generic progress notification event which may be
|
||
used by TGraphic classes with computationally intensive (slow)
|
||
operations, such as loading, storing, or transforming image data.
|
||
Event params:
|
||
Stage - Indicates whether this call to the OnProgress event is to
|
||
prepare for, process, or clean up after a graphic operation. If
|
||
OnProgress is called at all, the first call for a graphic operation
|
||
will be with Stage = psStarting, to allow the OnProgress event handler
|
||
to allocate whatever resources it needs to process subsequent progress
|
||
notifications. After Stage = psStarting, you are guaranteed that
|
||
OnProgress will be called again with Stage = psEnding to allow you
|
||
to free those resources, even if the graphic operation is aborted by
|
||
an exception. Zero or more calls to OnProgress with Stage = psRunning
|
||
may occur between the psStarting and psEnding calls.
|
||
PercentDone - The ratio of work done to work remaining, on a scale of
|
||
0 to 100. Values may repeat or even regress (get smaller) in
|
||
successive calls. PercentDone is usually only a guess, and the
|
||
guess may be dramatically altered as new information is discovered
|
||
in decoding the image.
|
||
RedrawNow - Indicates whether the graphic can be/should be redrawn
|
||
immediately. Useful for showing successive approximations of
|
||
an image as data is available instead of waiting for all the data
|
||
to arrive before drawing anything. Since there is no message loop
|
||
activity during graphic operations, you should call Update to force
|
||
a control to be redrawn immediately in the OnProgress event handler.
|
||
Redrawing a graphic when RedrawNow = False could corrupt the image
|
||
and/or cause exceptions.
|
||
Rect - Area of image that has changed and needs to be redrawn.
|
||
Msg - Optional text describing in one or two words what the graphic
|
||
class is currently working on. Ex: "Loading" "Storing"
|
||
"Reducing colors". The Msg string can also be empty.
|
||
Msg strings should be resourced for translation, should not
|
||
contain trailing periods, and should be used only for
|
||
display purposes. (do not: if Msg = 'Loading' then...)
|
||
}
|
||
|
||
TProgressStage = (psStarting, psRunning, psEnding);
|
||
TProgressEvent = procedure (Sender: TObject; Stage: TProgressStage;
|
||
PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string) of object;
|
||
|
||
{ The TGraphic class is a abstract base class for dealing with graphic images
|
||
such as metafile, bitmaps, icons, and other image formats.
|
||
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.
|
||
Palette - Color palette of image. Zero if graphic doesn't need/use palettes.
|
||
Transparent - Image does not completely cover its rectangular area
|
||
Width - The native, unstretched, width of the graphic.
|
||
OnChange - Called whenever the graphic changes
|
||
PaletteModified - Indicates in OnChange whether color palette has changed.
|
||
Stays true until whoever's responsible for realizing this new palette
|
||
(ex: TImage) sets it to False.
|
||
OnProgress - Generic progress indicator event. Propagates out to TPicture
|
||
and TImage OnProgress events.}
|
||
|
||
TGraphic = class(TInterfacedPersistent, IStreamPersist)
|
||
private
|
||
FOnChange: TNotifyEvent;
|
||
FOnProgress: TProgressEvent;
|
||
FModified: Boolean;
|
||
FTransparent: Boolean;
|
||
FPaletteModified: Boolean;
|
||
procedure SetModified(Value: Boolean);
|
||
protected
|
||
procedure Changed(Sender: TObject); virtual;
|
||
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 GetPalette: HPALETTE; virtual;
|
||
function GetTransparent: Boolean; virtual;
|
||
function GetWidth: Integer; virtual; abstract;
|
||
procedure Progress(Sender: TObject; Stage: TProgressStage;
|
||
PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string); dynamic;
|
||
procedure ReadData(Stream: TStream); virtual;
|
||
procedure SetHeight(Value: Integer); virtual; abstract;
|
||
procedure SetPalette(Value: HPALETTE); virtual;
|
||
procedure SetTransparent(Value: Boolean); virtual;
|
||
procedure SetWidth(Value: Integer); virtual; abstract;
|
||
procedure WriteData(Stream: TStream); virtual;
|
||
public
|
||
constructor Create; virtual;
|
||
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 Palette: HPALETTE read GetPalette write SetPalette;
|
||
property PaletteModified: Boolean read FPaletteModified write FPaletteModified;
|
||
property Transparent: Boolean read GetTransparent write SetTransparent;
|
||
property Width: Integer read GetWidth write SetWidth;
|
||
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
||
property OnProgress: TProgressEvent read FOnProgress write FOnProgress;
|
||
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.
|
||
UnRegisterGraphicClass - Removes all references to the specified TGraphic
|
||
class and all its descendents from the file format and clipboard format
|
||
internal lists.
|
||
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(TInterfacedPersistent, IStreamPersist)
|
||
private
|
||
FGraphic: TGraphic;
|
||
FOnChange: TNotifyEvent;
|
||
FNotify: IChangeNotifier;
|
||
FOnProgress: TProgressEvent;
|
||
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); dynamic;
|
||
procedure DefineProperties(Filer: TFiler); override;
|
||
procedure Progress(Sender: TObject; Stage: TProgressStage;
|
||
PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string); dynamic;
|
||
procedure LoadFromStream(Stream: TStream);
|
||
procedure SaveToStream(Stream: TStream);
|
||
public
|
||
constructor Create;
|
||
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);
|
||
class procedure UnregisterGraphicClass(AClass: TGraphicClass);
|
||
property Bitmap: TBitmap read GetBitmap write SetBitmap;
|
||
property Graphic: TGraphic read FGraphic write SetGraphic;
|
||
property PictureAdapter: IChangeNotifier read FNotify write FNotify;
|
||
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;
|
||
property OnProgress: TProgressEvent read FOnProgress write FOnProgress;
|
||
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;
|
||
MyMetafile.Width := 200;
|
||
MyMetafile.Height := 200;
|
||
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;
|
||
|
||
TSharedImage = class
|
||
private
|
||
FRefCount: Integer;
|
||
protected
|
||
procedure Reference;
|
||
procedure Release;
|
||
procedure FreeHandle; virtual; abstract;
|
||
property RefCount: Integer read FRefCount;
|
||
end;
|
||
|
||
TMetafileImage = class(TSharedImage)
|
||
private
|
||
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
|
||
protected
|
||
procedure FreeHandle; override;
|
||
public
|
||
destructor Destroy; override;
|
||
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;
|
||
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 GetPalette: HPALETTE; 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 SetTransparent(Value: Boolean); 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;
|
||
function HandleAllocated: Boolean;
|
||
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;
|
||
function ReleaseHandle: HENHMETAFILE;
|
||
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;
|
||
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 image. Creating copies of a TBitmap is very fast
|
||
since the handle 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 }
|
||
|
||
TBitmapImage = class(TSharedImage)
|
||
private
|
||
FHandle: HBITMAP; // DDB or DIB handle, used for drawing
|
||
FMaskHandle: HBITMAP; // DDB handle
|
||
FPalette: HPALETTE;
|
||
FDIBHandle: HBITMAP; // DIB handle corresponding to TDIBSection
|
||
FDIB: TDIBSection;
|
||
FSaveStream: TMemoryStream; // Save original RLE stream until image is modified
|
||
FOS2Format: Boolean; // Write BMP file header, color table in OS/2 format
|
||
FHalftone: Boolean; // FPalette is halftone; don't write to file
|
||
protected
|
||
procedure FreeHandle; override;
|
||
public
|
||
destructor Destroy; override;
|
||
end;
|
||
|
||
TBitmapHandleType = (bmDIB, bmDDB);
|
||
TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);
|
||
TTransparentMode = (tmAuto, tmFixed);
|
||
|
||
TBitmap = class(TGraphic)
|
||
private
|
||
FImage: TBitmapImage;
|
||
FCanvas: TCanvas;
|
||
FIgnorePalette: Boolean;
|
||
FMaskBitsValid: Boolean;
|
||
FMaskValid: Boolean;
|
||
FTransparentColor: TColor;
|
||
FTransparentMode: TTransparentMode;
|
||
procedure Changing(Sender: TObject);
|
||
procedure CopyImage(AHandle: HBITMAP; APalette: HPALETTE; DIB: TDIBSection);
|
||
procedure DIBNeeded;
|
||
procedure FreeContext;
|
||
function GetCanvas: TCanvas;
|
||
function GetHandle: HBITMAP; virtual;
|
||
function GetHandleType: TBitmapHandleType;
|
||
function GetMaskHandle: HBITMAP; virtual;
|
||
function GetMonochrome: Boolean;
|
||
function GetPixelFormat: TPixelFormat;
|
||
function GetScanline(Row: Integer): Pointer;
|
||
function GetTransparentColor: TColor;
|
||
procedure NewImage(NewHandle: HBITMAP; NewPalette: HPALETTE;
|
||
const NewDIB: TDIBSection; OS2Format: Boolean; RLEStream: TStream = nil);
|
||
procedure ReadStream(Stream: TStream; Size: Longint);
|
||
procedure ReadDIB(Stream: TStream; ImageSize: LongWord; bmf: PBitmapFileHeader = nil);
|
||
procedure SetHandle(Value: HBITMAP);
|
||
procedure SetHandleType(Value: TBitmapHandleType); virtual;
|
||
procedure SetMaskHandle(Value: HBITMAP);
|
||
procedure SetMonochrome(Value: Boolean);
|
||
procedure SetPixelFormat(Value: TPixelFormat);
|
||
procedure SetTransparentColor(Value: TColor);
|
||
procedure SetTransparentMode(Value: TTransparentMode);
|
||
function TransparentColorStored: Boolean;
|
||
procedure WriteStream(Stream: TStream; WriteSize: Boolean);
|
||
protected
|
||
procedure Changed(Sender: TObject); override;
|
||
procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
|
||
function GetEmpty: Boolean; override;
|
||
function GetHeight: Integer; override;
|
||
function GetPalette: HPALETTE; override;
|
||
function GetWidth: Integer; override;
|
||
procedure HandleNeeded;
|
||
procedure MaskHandleNeeded;
|
||
procedure PaletteNeeded;
|
||
procedure ReadData(Stream: TStream); override;
|
||
procedure SetHeight(Value: Integer); override;
|
||
procedure SetPalette(Value: HPALETTE); override;
|
||
procedure SetWidth(Value: Integer); override;
|
||
procedure WriteData(Stream: TStream); override;
|
||
public
|
||
constructor Create; override;
|
||
destructor Destroy; override;
|
||
procedure Assign(Source: TPersistent); override;
|
||
procedure Dormant;
|
||
procedure FreeImage;
|
||
function HandleAllocated: Boolean;
|
||
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
|
||
APalette: HPALETTE); override;
|
||
procedure LoadFromStream(Stream: TStream); override;
|
||
procedure LoadFromResourceName(Instance: THandle; const ResName: String);
|
||
{$IFDEF MSWINDOWS}
|
||
procedure LoadFromResourceID(Instance: THandle; ResID: Integer);
|
||
{$ENDIF}
|
||
procedure Mask(TransparentColor: TColor);
|
||
function ReleaseHandle: HBITMAP;
|
||
function ReleaseMaskHandle: 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 HandleType: TBitmapHandleType read GetHandleType write SetHandleType;
|
||
property IgnorePalette: Boolean read FIgnorePalette write FIgnorePalette;
|
||
property MaskHandle: HBITMAP read GetMaskHandle write SetMaskHandle;
|
||
property Monochrome: Boolean read GetMonochrome write SetMonochrome;
|
||
property PixelFormat: TPixelFormat read GetPixelFormat write SetPixelFormat;
|
||
property ScanLine[Row: Integer]: Pointer read GetScanLine;
|
||
property TransparentColor: TColor read GetTransparentColor
|
||
write SetTransparentColor stored TransparentColorStored;
|
||
property TransparentMode: TTransparentMode read FTransparentMode
|
||
write SetTransparentMode default tmAuto;
|
||
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(TSharedImage)
|
||
private
|
||
FHandle: HICON;
|
||
FMemoryImage: TCustomMemoryStream;
|
||
FSize: TPoint;
|
||
protected
|
||
procedure FreeHandle; override;
|
||
public
|
||
destructor Destroy; override;
|
||
end;
|
||
|
||
TIcon = class(TGraphic)
|
||
private
|
||
FImage: TIconImage;
|
||
FRequestedSize: TPoint;
|
||
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 SetTransparent(Value: Boolean); override;
|
||
procedure SetWidth(Value: Integer); override;
|
||
public
|
||
constructor Create; override;
|
||
destructor Destroy; override;
|
||
procedure Assign(Source: TPersistent); override;
|
||
function HandleAllocated: Boolean;
|
||
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle;
|
||
APalette: HPALETTE); override;
|
||
procedure LoadFromStream(Stream: TStream); override;
|
||
function ReleaseHandle: HICON;
|
||
procedure SaveToClipboardFormat(var Format: Word; var Data: THandle;
|
||
var APalette: HPALETTE); override;
|
||
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');
|
||
|
||
var
|
||
SystemPalette16: HPalette; // 16 color palette that maps to the system palette
|
||
|
||
var
|
||
DDBsOnly: Boolean = False; // True = Load all BMPs as device bitmaps.
|
||
// Not recommended.
|
||
|
||
function GraphicFilter(GraphicClass: TGraphicClass): string;
|
||
function GraphicExtension(GraphicClass: TGraphicClass): string;
|
||
function GraphicFileMask(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;
|
||
|
||
procedure GetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: DWORD;
|
||
var ImageSize: DWORD);
|
||
function GetDIB(Bitmap: HBITMAP; Palette: HPALETTE; var BitmapInfo; var Bits): Boolean;
|
||
|
||
function CopyPalette(Palette: HPALETTE): HPALETTE;
|
||
|
||
procedure PaletteChanged;
|
||
procedure FreeMemoryContexts;
|
||
|
||
function GetDefFontCharSet: TFontCharSet;
|
||
|
||
function TransparentStretchBlt(DstDC: HDC; DstX, DstY, DstW, DstH: Integer;
|
||
SrcDC: HDC; SrcX, SrcY, SrcW, SrcH: Integer; MaskDC: HDC; MaskX,
|
||
MaskY: Integer): Boolean;
|
||
|
||
function CreateMappedBmp(Handle: HBITMAP; const OldColors, NewColors: array of TColor): HBITMAP;
|
||
function CreateMappedRes(Instance: THandle; ResName: PChar; const OldColors, NewColors: array of TColor): HBITMAP;
|
||
function CreateGrayMappedBmp(Handle: HBITMAP): HBITMAP;
|
||
function CreateGrayMappedRes(Instance: THandle; ResName: PChar): HBITMAP;
|
||
|
||
function AllocPatternBitmap(BkColor, FgColor: TColor): TBitmap;
|
||
|
||
// Alignment must be a power of 2. Color BMPs require DWORD alignment (32).
|
||
function BytesPerScanline(PixelsPerScanline, BitsPerPixel, Alignment: Longint): Longint;
|
||
|
||
var PenRatio: Double =1;
|
||
|
||
implementation
|
||
|
||
{ Things left out
|
||
---------------
|
||
Regions
|
||
PatBlt
|
||
Tabbed text
|
||
Clipping regions
|
||
Coordinate transformations
|
||
Paths
|
||
Beziers }
|
||
|
||
uses Consts;
|
||
|
||
const
|
||
csAllValid = [csHandleValid..csBrushValid];
|
||
|
||
var
|
||
ScreenLogPixels: Integer;
|
||
StockPen: HPEN;
|
||
StockBrush: HBRUSH;
|
||
StockFont: HFONT;
|
||
StockIcon: HICON;
|
||
BitmapImageLock: TRTLCriticalSection;
|
||
CounterLock: TRTLCriticalSection;
|
||
|
||
procedure InternalDeletePalette(Pal: HPalette);
|
||
begin
|
||
if (Pal <> 0) and (Pal <> SystemPalette16) then
|
||
DeleteObject(Pal);
|
||
end;
|
||
|
||
{ Resource managers }
|
||
|
||
const
|
||
ResInfoSize = SizeOf(TResource) - SizeOf(TFontData);
|
||
|
||
type
|
||
TResourceManager = class(TMyObject)
|
||
ResList: PResource;
|
||
FLock: TRTLCriticalSection;
|
||
ResDataSize: Word;
|
||
constructor Create(AResDataSize: Word);
|
||
destructor Destroy; override;
|
||
function AllocResource(const ResData): PResource;
|
||
procedure FreeResource(Resource: PResource);
|
||
procedure ChangeResource(GraphicsObject: TGraphicsObject; const ResData);
|
||
procedure AssignResource(GraphicsObject: TGraphicsObject;
|
||
AResource: PResource);
|
||
procedure Lock;
|
||
procedure Unlock;
|
||
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;
|
||
|
||
constructor TResourceManager.Create(AResDataSize: Word);
|
||
begin
|
||
ResDataSize := AResDataSize;
|
||
InitializeCriticalSection(FLock);
|
||
end;
|
||
|
||
destructor TResourceManager.Destroy;
|
||
begin
|
||
DeleteCriticalSection(FLock);
|
||
end;
|
||
|
||
procedure TResourceManager.Lock;
|
||
begin
|
||
EnterCriticalSection(FLock);
|
||
end;
|
||
|
||
procedure TResourceManager.Unlock;
|
||
begin
|
||
LeaveCriticalSection(FLock);
|
||
end;
|
||
|
||
function TResourceManager.AllocResource(const ResData): PResource;
|
||
var
|
||
ResHash: Word;
|
||
begin
|
||
ResHash := GetHashCode(ResData, ResDataSize);
|
||
Lock;
|
||
try
|
||
Result := ResList;
|
||
while (Result <> nil) and ((Result^.HashCode <> ResHash) or
|
||
not CompareMem(@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);
|
||
finally
|
||
Unlock;
|
||
end;
|
||
end;
|
||
|
||
procedure TResourceManager.FreeResource(Resource: PResource);
|
||
var
|
||
P: PResource;
|
||
DeleteIt: Boolean;
|
||
begin
|
||
if Resource <> nil then
|
||
with Resource^ do
|
||
begin
|
||
Lock;
|
||
try
|
||
Dec(RefCount);
|
||
DeleteIt := RefCount = 0;
|
||
if DeleteIt then
|
||
begin
|
||
if Resource = ResList then
|
||
ResList := Resource^.Next
|
||
else
|
||
begin
|
||
P := ResList;
|
||
while P^.Next <> Resource do P := P^.Next;
|
||
P^.Next := Resource^.Next;
|
||
end;
|
||
end;
|
||
finally
|
||
Unlock;
|
||
end;
|
||
if DeleteIt then
|
||
begin // this is outside the critsect to minimize lock time
|
||
if Handle <> 0 then DeleteObject(Handle);
|
||
FreeMem(Resource);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TResourceManager.ChangeResource(GraphicsObject: TGraphicsObject;
|
||
const ResData);
|
||
var
|
||
P: PResource;
|
||
begin
|
||
Lock;
|
||
try // prevent changes to GraphicsObject.FResource pointer between steps
|
||
P := GraphicsObject.FResource;
|
||
GraphicsObject.FResource := AllocResource(ResData);
|
||
if GraphicsObject.FResource <> P then GraphicsObject.Changed;
|
||
FreeResource(P);
|
||
finally
|
||
Unlock;
|
||
end;
|
||
end;
|
||
|
||
procedure TResourceManager.AssignResource(GraphicsObject: TGraphicsObject;
|
||
AResource: PResource);
|
||
var
|
||
P: PResource;
|
||
begin
|
||
Lock;
|
||
try
|
||
P := GraphicsObject.FResource;
|
||
if P <> AResource then
|
||
begin
|
||
Inc(AResource^.RefCount);
|
||
GraphicsObject.FResource := AResource;
|
||
GraphicsObject.Changed;
|
||
FreeResource(P);
|
||
end;
|
||
finally
|
||
Unlock;
|
||
end;
|
||
end;
|
||
|
||
var
|
||
CanvasList: TThreadList;
|
||
|
||
procedure PaletteChanged;
|
||
|
||
procedure ClearColor(ResMan: TResourceManager);
|
||
var
|
||
Resource: PResource;
|
||
begin
|
||
ResMan.Lock;
|
||
try
|
||
Resource := ResMan.ResList;
|
||
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;
|
||
finally
|
||
ResMan.Unlock;
|
||
end;
|
||
end;
|
||
|
||
var
|
||
I,J: Integer;
|
||
begin
|
||
{ Called when the system palette has changed (WM_SYSCOLORCHANGE) }
|
||
I := 0;
|
||
with CanvasList.LockList do
|
||
try
|
||
while I < Count do
|
||
begin
|
||
with TCanvas(Items[I]) do
|
||
begin
|
||
Lock;
|
||
Inc(I);
|
||
DeselectHandles;
|
||
end;
|
||
end;
|
||
ClearColor(PenManager);
|
||
ClearColor(BrushManager);
|
||
finally
|
||
for J := 0 to I-1 do // Only unlock the canvases we actually locked
|
||
TCanvas(Items[J]).Unlock;
|
||
CanvasList.UnlockList;
|
||
end;
|
||
end;
|
||
|
||
{ Color mapping routines }
|
||
|
||
const
|
||
Colors: array[0..51] of TIdentMapEntry = (
|
||
(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: clMoneyGreen; Name: 'clMoneyGreen'),
|
||
(Value: clSkyBlue; Name: 'clSkyBlue'),
|
||
(Value: clCream; Name: 'clCream'),
|
||
(Value: clMedGray; Name: 'clMedGray'),
|
||
|
||
(Value: clActiveBorder; Name: 'clActiveBorder'),
|
||
(Value: clActiveCaption; Name: 'clActiveCaption'),
|
||
(Value: clAppWorkSpace; Name: 'clAppWorkSpace'),
|
||
(Value: clBackground; Name: 'clBackground'),
|
||
(Value: clBtnFace; Name: 'clBtnFace'),
|
||
(Value: clBtnHighlight; Name: 'clBtnHighlight'),
|
||
(Value: clBtnShadow; Name: 'clBtnShadow'),
|
||
(Value: clBtnText; Name: 'clBtnText'),
|
||
(Value: clCaptionText; Name: 'clCaptionText'),
|
||
(Value: clDefault; Name: 'clDefault'),
|
||
(Value: clGradientActiveCaption; Name: 'clGradientActiveCaption'),
|
||
(Value: clGradientInactiveCaption; Name: 'clGradientInactiveCaption'),
|
||
(Value: clGrayText; Name: 'clGrayText'),
|
||
(Value: clHighlight; Name: 'clHighlight'),
|
||
(Value: clHighlightText; Name: 'clHighlightText'),
|
||
(Value: clHotLight; Name: 'clHotLight'),
|
||
(Value: clInactiveBorder; Name: 'clInactiveBorder'),
|
||
(Value: clInactiveCaption; Name: 'clInactiveCaption'),
|
||
(Value: clInactiveCaptionText; Name: 'clInactiveCaptionText'),
|
||
(Value: clInfoBk; Name: 'clInfoBk'),
|
||
(Value: clInfoText; Name: 'clInfoText'),
|
||
(Value: clMenu; Name: 'clMenu'),
|
||
(Value: clMenuBar; Name: 'clMenuBar'),
|
||
(Value: clMenuHighlight; Name: 'clMenuHighlight'),
|
||
(Value: clMenuText; Name: 'clMenuText'),
|
||
(Value: clNone; Name: 'clNone'),
|
||
(Value: clScrollBar; Name: 'clScrollBar'),
|
||
(Value: cl3DDkShadow; Name: 'cl3DDkShadow'),
|
||
(Value: cl3DLight; Name: 'cl3DLight'),
|
||
(Value: clWindow; Name: 'clWindow'),
|
||
(Value: clWindowFrame; Name: 'clWindowFrame'),
|
||
(Value: clWindowText; Name: 'clWindowText'));
|
||
|
||
|
||
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, '%s%.8x', [HexDisplayPrefix, 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;
|
||
begin
|
||
Result := IntToIdent(Color, Ident, Colors);
|
||
end;
|
||
|
||
function IdentToColor(const Ident: string; var Color: Longint): Boolean;
|
||
begin
|
||
Result := IdentToInt(Ident, Color, Colors);
|
||
end;
|
||
|
||
{ TGraphicsObject }
|
||
|
||
procedure TGraphicsObject.Changed;
|
||
begin
|
||
if Assigned(FOnChange) then FOnChange(Self);
|
||
end;
|
||
|
||
procedure TGraphicsObject.Lock;
|
||
begin
|
||
if Assigned(FOwnerLock) then EnterCriticalSection(FOwnerLock^);
|
||
end;
|
||
|
||
procedure TGraphicsObject.Unlock;
|
||
begin
|
||
if Assigned(FOwnerLock) then LeaveCriticalSection(FOwnerLock^);
|
||
end;
|
||
|
||
function TGraphicsObject.HandleAllocated: Boolean;
|
||
begin
|
||
Result := (FResource <> nil) and (FResource^.Handle <> 0);
|
||
end;
|
||
|
||
{ TFont }
|
||
|
||
const
|
||
FontCharsets: array[0..17] of TIdentMapEntry = (
|
||
(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;
|
||
begin
|
||
Result := IntToIdent(Charset, Ident, FontCharsets);
|
||
end;
|
||
|
||
function IdentToCharset(const Ident: string; var Charset: Longint): Boolean;
|
||
begin
|
||
Result := IdentToInt(Ident, CharSet, FontCharsets);
|
||
end;
|
||
|
||
function GetFontData(Font: HFont): TFontData;
|
||
var
|
||
LogFont: TLogFont;
|
||
begin
|
||
Result := DefFontData;
|
||
if Font <> 0 then
|
||
begin
|
||
if GetObject(Font, SizeOf(LogFont), @LogFont) <> 0 then
|
||
with Result, LogFont do
|
||
begin
|
||
Height := lfHeight;
|
||
if lfWeight >= FW_BOLD then
|
||
Include(Style, fsBold);
|
||
if lfItalic = 1 then
|
||
Include(Style, fsItalic);
|
||
if lfUnderline = 1 then
|
||
Include(Style, fsUnderline);
|
||
if lfStrikeOut = 1 then
|
||
Include(Style, fsStrikeOut);
|
||
Charset := TFontCharset(lfCharSet);
|
||
Name := lfFaceName;
|
||
case lfPitchAndFamily and $F of
|
||
VARIABLE_PITCH: Pitch := fpVariable;
|
||
FIXED_PITCH: Pitch := fpFixed;
|
||
else
|
||
Pitch := fpDefault;
|
||
end;
|
||
Handle := Font;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
constructor TFont.Create;
|
||
begin
|
||
DefFontData.Handle := 0;
|
||
FResource := FontManager.AllocResource(DefFontData);
|
||
FColor := clWindowText;
|
||
FPixelsPerInch := ScreenLogPixels;
|
||
end;
|
||
|
||
destructor TFont.Destroy;
|
||
begin
|
||
FontManager.FreeResource(FResource);
|
||
end;
|
||
|
||
procedure TFont.Changed;
|
||
begin
|
||
inherited Changed;
|
||
if FNotify <> nil then FNotify.Changed;
|
||
end;
|
||
|
||
procedure TFont.Assign(Source: TPersistent);
|
||
begin
|
||
if Source is TFont then
|
||
begin
|
||
Lock;
|
||
try
|
||
TFont(Source).Lock;
|
||
try
|
||
FontManager.AssignResource(Self, TFont(Source).FResource);
|
||
Color := TFont(Source).Color;
|
||
if PixelsPerInch <> TFont(Source).PixelsPerInch then
|
||
Size := TFont(Source).Size;
|
||
finally
|
||
TFont(Source).Unlock;
|
||
end;
|
||
finally
|
||
Unlock;
|
||
end;
|
||
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
|
||
Lock;
|
||
try
|
||
FontManager.ChangeResource(Self, FontData);
|
||
finally
|
||
Unlock;
|
||
end;
|
||
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
|
||
FontManager.Lock;
|
||
with LogFont do
|
||
try
|
||
if Handle = 0 then
|
||
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);
|
||
if AnsiCompareText(Font.Name, 'Default') = 0 then // do not localize
|
||
StrPCopy(lfFaceName, DefFontData.Name)
|
||
else
|
||
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;
|
||
Handle := CreateFontIndirect(LogFont);
|
||
end;
|
||
finally
|
||
FontManager.Unlock;
|
||
end;
|
||
end;
|
||
Result := Handle;
|
||
end;
|
||
end;
|
||
|
||
procedure TFont.SetHandle(Value: HFont);
|
||
begin
|
||
SetData(GetFontData(Value));
|
||
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;
|
||
|
||
//FRatio := 1;
|
||
|
||
end;
|
||
|
||
destructor TPen.Destroy;
|
||
begin
|
||
PenManager.FreeResource(FResource);
|
||
end;
|
||
|
||
procedure TPen.Assign(Source: TPersistent);
|
||
begin
|
||
if Source is TPen then
|
||
begin
|
||
Lock;
|
||
try
|
||
TPen(Source).Lock;
|
||
try
|
||
PenManager.AssignResource(Self, TPen(Source).FResource);
|
||
SetMode(TPen(Source).FMode);
|
||
finally
|
||
TPen(Source).Unlock;
|
||
end;
|
||
finally
|
||
Unlock;
|
||
end;
|
||
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
|
||
Lock;
|
||
try
|
||
PenManager.ChangeResource(Self, PenData);
|
||
finally
|
||
Unlock;
|
||
end;
|
||
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
|
||
PenManager.Lock;
|
||
with LogPen do
|
||
try
|
||
if Handle = 0 then
|
||
begin
|
||
lopnStyle := PenStyles[Pen.Style];
|
||
lopnWidth.X := Pen.Width;
|
||
lopnColor := ColorToRGB(Pen.Color);
|
||
Handle := CreatePenIndirect(LogPen);
|
||
end;
|
||
finally
|
||
PenManager.Unlock;
|
||
end;
|
||
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;
|
||
if (PenRatio > 1) then
|
||
begin
|
||
PenData.Width := Round(Value*PenRatio);
|
||
end else if PenRatio > 1 then begin
|
||
PenData.Width := Round(Value*(PenRatio/2));
|
||
end;
|
||
SetData(PenData);
|
||
end;
|
||
end;
|
||
|
||
|
||
constructor TExtPen.Create;
|
||
begin
|
||
inherited Create;
|
||
FGeometric := False;
|
||
end;
|
||
|
||
function TExtPen.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;
|
||
lb: TLogBrush;
|
||
begin
|
||
with FResource^ do
|
||
begin
|
||
if Handle = 0 then
|
||
begin
|
||
PenManager.Lock;
|
||
with LogPen do
|
||
try
|
||
if Handle = 0 then
|
||
begin
|
||
lopnStyle := PenStyles[Pen.Style];
|
||
lopnWidth.X := Pen.Width;
|
||
lopnColor := ColorToRGB(Pen.Color);
|
||
|
||
if FGeometric and (Self.Mode <> pmXor) and (pen.width>1) and (pen.Style <> psSOlid) then begin
|
||
lb.lbStyle := BS_SOLID;
|
||
lb.lbColor := ColorToRGB(Pen.Color);
|
||
lb.lbHatch := 0;
|
||
Handle := ExtCreatePen(PS_GEOMETRIC or lopnStyle or
|
||
PS_ENDCAP_SQUARE or PS_JOIN_BEVEL,pen.width,lb,0,nil);
|
||
end else begin
|
||
Handle := CreatePenIndirect(LogPen);
|
||
end;
|
||
end;
|
||
finally
|
||
PenManager.Unlock;
|
||
end;
|
||
end;
|
||
Result := Handle;
|
||
end;
|
||
end;
|
||
|
||
procedure TExtPen.SetGeometric(const Value: Boolean);
|
||
begin
|
||
FGeometric := Value;
|
||
Changed;
|
||
end;
|
||
|
||
procedure TExtPen.SetHandle(Value: HPen);
|
||
var
|
||
PenData: TPenData;
|
||
begin
|
||
PenData := DefPenData;
|
||
PenData.Handle := Value;
|
||
SetData(PenData);
|
||
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
|
||
Lock;
|
||
try
|
||
TBrush(Source).Lock;
|
||
try
|
||
BrushManager.AssignResource(Self, TBrush(Source).FResource);
|
||
finally
|
||
TBrush(Source).Unlock;
|
||
end;
|
||
finally
|
||
Unlock;
|
||
end;
|
||
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
|
||
Lock;
|
||
try
|
||
BrushManager.ChangeResource(Self, BrushData);
|
||
finally
|
||
Unlock;
|
||
end;
|
||
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
|
||
BrushManager.Lock;
|
||
try
|
||
if Handle = 0 then
|
||
begin
|
||
with LogBrush do
|
||
begin
|
||
if Brush.Bitmap <> nil then
|
||
begin
|
||
lbStyle := BS_PATTERN;
|
||
Brush.Bitmap.HandleType := bmDDB;
|
||
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;
|
||
finally
|
||
BrushManager.Unlock;
|
||
end;
|
||
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;
|
||
|
||
{ TFontRecall }
|
||
|
||
constructor TFontRecall.Create(AFont: TFont);
|
||
begin
|
||
inherited Create(TFont.Create, AFont);
|
||
end;
|
||
|
||
{ TPenRecall }
|
||
|
||
constructor TPenRecall.Create(APen: TPen);
|
||
begin
|
||
inherited Create(TPen.Create, APen);
|
||
end;
|
||
|
||
{ TBrushRecall }
|
||
|
||
constructor TBrushRecall.Create(ABrush: TBrush);
|
||
begin
|
||
inherited Create(TBrush.Create, ABrush);
|
||
end;
|
||
|
||
{ TCanvas }
|
||
|
||
constructor TCanvas.Create;
|
||
begin
|
||
inherited Create;
|
||
InitializeCriticalSection(FLock);
|
||
FFont := TFont.Create;
|
||
FFont.OnChange := FontChanged;
|
||
FFont.OwnerCriticalSection := @FLock;
|
||
FPen := TExtPen.Create;
|
||
FPen.OnChange := PenChanged;
|
||
FPen.OwnerCriticalSection := @FLock;
|
||
FBrush := TBrush.Create;
|
||
FBrush.OnChange := BrushChanged;
|
||
FBrush.OwnerCriticalSection := @FLock;
|
||
FCopyMode := cmSrcCopy;
|
||
State := [];
|
||
CanvasList.Add(Self);
|
||
end;
|
||
|
||
destructor TCanvas.Destroy;
|
||
begin
|
||
CanvasList.Remove(Self);
|
||
SetHandle(0);
|
||
FFont.Free;
|
||
FPen.Free;
|
||
FBrush.Free;
|
||
DeleteCriticalSection(FLock);
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TCanvas.Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer);
|
||
begin
|
||
Changing;
|
||
RequiredState([csHandleValid, csPenValid, csBrushValid]);
|
||
Windows.Arc(FHandle, X1, Y1, X2, Y2, X3, Y3, X4, Y4);
|
||
Changed;
|
||
end;
|
||
|
||
procedure TCanvas.BrushCopy(const Dest: TRect; Bitmap: TBitmap;
|
||
const Source: TRect; Color: TColor);
|
||
const
|
||
ROP_DSPDxax = $00E20746;
|
||
var
|
||
SrcW, SrcH, DstW, DstH: Integer;
|
||
crBack, crText: TColorRef;
|
||
MaskDC: HDC;
|
||
Mask: TBitmap;
|
||
MaskHandle: HBITMAP;
|
||
begin
|
||
if Bitmap = nil then Exit;
|
||
Lock;
|
||
try
|
||
Changing;
|
||
RequiredState([csHandleValid, csBrushValid]);
|
||
Bitmap.Canvas.Lock;
|
||
try
|
||
DstW := Dest.Right - Dest.Left;
|
||
DstH := Dest.Bottom - Dest.Top;
|
||
SrcW := Source.Right - Source.Left;
|
||
SrcH := Source.Bottom - Source.Top;
|
||
|
||
if Bitmap.TransparentColor = Color then
|
||
begin
|
||
Mask := nil;
|
||
MaskHandle := Bitmap.MaskHandle;
|
||
MaskDC := CreateCompatibleDC(0);
|
||
MaskHandle := SelectObject(MaskDC, MaskHandle);
|
||
end
|
||
else
|
||
begin
|
||
Mask := TBitmap.Create;
|
||
Mask.Assign(Bitmap);
|
||
{ Replace Color with black and all other colors with white }
|
||
Mask.Mask(Color);
|
||
Mask.Canvas.RequiredState([csHandleValid]);
|
||
MaskDC := Mask.Canvas.FHandle;
|
||
MaskHandle := 0;
|
||
end;
|
||
|
||
try
|
||
Bitmap.Canvas.RequiredState([csHandleValid]);
|
||
{ Draw transparently or use brush color to fill background }
|
||
if Brush.Style = bsClear then
|
||
begin
|
||
TransparentStretchBlt(FHandle, Dest.Left, Dest.Top, DstW, DstH,
|
||
Bitmap.Canvas.FHandle, Source.Left, Source.Top, SrcW, SrcH,
|
||
MaskDC, Source.Left, Source.Top);
|
||
end
|
||
else
|
||
begin
|
||
StretchBlt(FHandle, Dest.Left, Dest.Top, DstW, DstH,
|
||
Bitmap.Canvas.FHandle, Source.Left, Source.Top, SrcW, SrcH, SrcCopy);
|
||
crText := SetTextColor(Self.FHandle, 0);
|
||
crBack := SetBkColor(Self.FHandle, $FFFFFF);
|
||
StretchBlt(Self.FHandle, Dest.Left, Dest.Top, DstW, DstH,
|
||
MaskDC, Source.Left, Source.Top, SrcW, SrcH, ROP_DSPDxax);
|
||
SetTextColor(Self.FHandle, crText);
|
||
SetBkColor(Self.FHandle, crBack);
|
||
end;
|
||
finally
|
||
if Assigned(Mask) then Mask.Free
|
||
else
|
||
begin
|
||
if MaskHandle <> 0 then SelectObject(MaskDC, MaskHandle);
|
||
DeleteDC(MaskDC);
|
||
end;
|
||
end;
|
||
finally
|
||
Bitmap.Canvas.Unlock;
|
||
end;
|
||
Changed;
|
||
finally
|
||
Unlock;
|
||
end;
|
||
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.Ellipse(const Rect: TRect);
|
||
begin
|
||
Ellipse(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
|
||
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;
|
||
|
||
function TCanvas.HandleAllocated: Boolean;
|
||
begin
|
||
Result := FHandle <> 0;
|
||
end;
|
||
|
||
procedure TCanvas.LineTo(X, Y: Integer);
|
||
begin
|
||
Changing;
|
||
RequiredState([csHandleValid, csPenValid, csBrushValid]);
|
||
Windows.LineTo(FHandle, X, Y);
|
||
Changed;
|
||
end;
|
||
|
||
procedure TCanvas.Lock;
|
||
begin
|
||
EnterCriticalSection(CounterLock);
|
||
Inc(FLockCount);
|
||
LeaveCriticalSection(CounterLock);
|
||
EnterCriticalSection(FLock);
|
||
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.PolyBezier(const Points: array of TPoint);
|
||
begin
|
||
Changing;
|
||
RequiredState([csHandleValid, csPenValid, csBrushValid]);
|
||
Windows.PolyBezier(FHandle, PPoints(@Points)^, High(Points) + 1);
|
||
Changed;
|
||
end;
|
||
|
||
procedure TCanvas.PolyBezierTo(const Points: array of TPoint);
|
||
begin
|
||
Changing;
|
||
RequiredState([csHandleValid, csPenValid, csBrushValid]);
|
||
Windows.PolyBezierTo(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.Rectangle(const Rect: TRect);
|
||
begin
|
||
Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
|
||
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;
|
||
|
||
function TCanvas.GetCanvasOrientation: TCanvasOrientation;
|
||
var
|
||
Point: TPoint;
|
||
begin
|
||
Result := coLeftToRight;
|
||
if (FTextFlags and ETO_RTLREADING) <> 0 then
|
||
begin
|
||
GetWindowOrgEx(Handle, Point);
|
||
if Point.X <> 0 then Result := coRightToLeft
|
||
end;
|
||
end;
|
||
|
||
procedure TCanvas.TextOut(X, Y: Integer; const Text: String);
|
||
begin
|
||
Changing;
|
||
RequiredState([csHandleValid, csFontValid, csBrushValid]);
|
||
if CanvasOrientation = coRightToLeft then Inc(X, TextWidth(Text) + 1);
|
||
Windows.ExtTextOut(FHandle, X, Y, FTextFlags, nil, PChar(Text),
|
||
Length(Text), nil);
|
||
MoveTo(X + TextWidth(Text), Y);
|
||
Changed;
|
||
end;
|
||
|
||
procedure TCanvas.TextRect(Rect: TRect; X, Y: Integer; const Text: string);
|
||
var
|
||
Options: Longint;
|
||
begin
|
||
Changing;
|
||
RequiredState([csHandleValid, csFontValid, csBrushValid]);
|
||
Options := ETO_CLIPPED or FTextFlags;
|
||
if Brush.Style <> bsClear then
|
||
Options := Options or ETO_OPAQUE;
|
||
if ((FTextFlags and ETO_RTLREADING) <> 0) and
|
||
(CanvasOrientation = coRightToLeft) then Inc(X, TextWidth(Text) + 1);
|
||
Windows.ExtTextOut(FHandle, X, Y, Options, @Rect, PChar(Text),
|
||
Length(Text), nil);
|
||
Changed;
|
||
end;
|
||
|
||
function TCanvas.TextExtent(const Text: string): TSize;
|
||
begin
|
||
RequiredState([csHandleValid, csFontValid]);
|
||
Result.cX := 0;
|
||
Result.cY := 0;
|
||
Windows.GetTextExtentPoint32(FHandle, PChar(Text), Length(Text), Result);
|
||
end;
|
||
|
||
function TCanvas.TextWidth(const Text: string): Integer;
|
||
begin
|
||
Result := TextExtent(Text).cX;
|
||
end;
|
||
|
||
function TCanvas.TextHeight(const Text: string): Integer;
|
||
begin
|
||
Result := TextExtent(Text).cY;
|
||
end;
|
||
|
||
function TCanvas.TryLock: Boolean;
|
||
begin
|
||
EnterCriticalSection(CounterLock);
|
||
try
|
||
Result := FLockCount = 0;
|
||
if Result then Lock;
|
||
finally
|
||
LeaveCriticalSection(CounterLock);
|
||
end;
|
||
end;
|
||
|
||
procedure TCanvas.Unlock;
|
||
begin
|
||
LeaveCriticalSection(FLock);
|
||
EnterCriticalSection(CounterLock);
|
||
Dec(FLockCount);
|
||
LeaveCriticalSection(CounterLock);
|
||
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 CreatePen;
|
||
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, TExtPen(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 }
|
||
|
||
{ Metafile types }
|
||
|
||
const
|
||
WMFKey = Integer($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: PResStringRec);
|
||
begin
|
||
raise EInvalidGraphicOperation.CreateRes(Str);
|
||
end;
|
||
|
||
procedure InvalidGraphic(Str: PResStringRec);
|
||
begin
|
||
raise EInvalidGraphic.CreateRes(Str);
|
||
end;
|
||
|
||
procedure InvalidBitmap;
|
||
begin
|
||
InvalidGraphic(@SInvalidBitmap);
|
||
end;
|
||
|
||
procedure InvalidIcon;
|
||
begin
|
||
InvalidGraphic(@SInvalidIcon);
|
||
end;
|
||
|
||
procedure InvalidMetafile;
|
||
begin
|
||
InvalidGraphic(@SInvalidMetafile);
|
||
end;
|
||
|
||
procedure OutOfResources;
|
||
begin
|
||
raise EOutOfResources.Create(SOutOfResources);
|
||
end;
|
||
|
||
procedure GDIError;
|
||
var
|
||
ErrorCode: Integer;
|
||
Buf: array [Byte] of Char;
|
||
begin
|
||
ErrorCode := GetLastError;
|
||
if (ErrorCode <> 0) and (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil,
|
||
ErrorCode, LOCALE_USER_DEFAULT, Buf, sizeof(Buf), nil) <> 0) then
|
||
raise EOutOfResources.Create(Buf)
|
||
else
|
||
OutOfResources;
|
||
end;
|
||
|
||
function GDICheck(Value: Integer): Integer;
|
||
begin
|
||
if Value = 0 then GDIError;
|
||
Result := Value;
|
||
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);
|
||
|
||
try
|
||
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 GDIError;
|
||
try
|
||
Result := CreateCompatibleBitmap(DC, Size.X, Size.Y);
|
||
if Result = 0 then GDIError;
|
||
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;
|
||
finally
|
||
DeleteDC(Mem1);
|
||
DeleteDC(Mem2);
|
||
end;
|
||
end;
|
||
|
||
function GetDInColors(BitCount: Word): Integer;
|
||
begin
|
||
case BitCount of
|
||
1, 4, 8: Result := 1 shl BitCount;
|
||
else
|
||
Result := 0;
|
||
end;
|
||
end;
|
||
|
||
function BytesPerScanline(PixelsPerScanline, BitsPerPixel, Alignment: Longint): Longint;
|
||
begin
|
||
Dec(Alignment);
|
||
Result := ((PixelsPerScanline * BitsPerPixel) + Alignment) and not Alignment;
|
||
Result := Result div 8;
|
||
end;
|
||
|
||
function TransparentStretchBlt(DstDC: HDC; DstX, DstY, DstW, DstH: Integer;
|
||
SrcDC: HDC; SrcX, SrcY, SrcW, SrcH: Integer; MaskDC: HDC; MaskX,
|
||
MaskY: Integer): Boolean;
|
||
const
|
||
ROP_DstCopy = $00AA0029;
|
||
var
|
||
MemDC: HDC;
|
||
MemBmp: HBITMAP;
|
||
Save: THandle;
|
||
crText, crBack: TColorRef;
|
||
SavePal: HPALETTE;
|
||
begin
|
||
Result := True;
|
||
if (Win32Platform = VER_PLATFORM_WIN32_NT) and (SrcW = DstW) and (SrcH = DstH) then
|
||
begin
|
||
MemBmp := GDICheck(CreateCompatibleBitmap(SrcDC, 1, 1));
|
||
MemBmp := SelectObject(MaskDC, MemBmp);
|
||
try
|
||
MaskBlt(DstDC, DstX, DstY, DstW, DstH, SrcDC, SrcX, SrcY, MemBmp, MaskX,
|
||
MaskY, MakeRop4(ROP_DstCopy, SrcCopy));
|
||
finally
|
||
MemBmp := SelectObject(MaskDC, MemBmp);
|
||
DeleteObject(MemBmp);
|
||
end;
|
||
Exit;
|
||
end;
|
||
SavePal := 0;
|
||
MemDC := GDICheck(CreateCompatibleDC(0));
|
||
try
|
||
MemBmp := GDICheck(CreateCompatibleBitmap(SrcDC, SrcW, SrcH));
|
||
Save := SelectObject(MemDC, MemBmp);
|
||
SavePal := SelectPalette(SrcDC, SystemPalette16, False);
|
||
SelectPalette(SrcDC, SavePal, False);
|
||
if SavePal <> 0 then
|
||
SavePal := SelectPalette(MemDC, SavePal, True)
|
||
else
|
||
SavePal := SelectPalette(MemDC, SystemPalette16, True);
|
||
RealizePalette(MemDC);
|
||
|
||
StretchBlt(MemDC, 0, 0, SrcW, SrcH, MaskDC, MaskX, MaskY, SrcW, SrcH, SrcCopy);
|
||
StretchBlt(MemDC, 0, 0, SrcW, SrcH, SrcDC, SrcX, SrcY, SrcW, SrcH, SrcErase);
|
||
crText := SetTextColor(DstDC, $0);
|
||
crBack := SetBkColor(DstDC, $FFFFFF);
|
||
StretchBlt(DstDC, DstX, DstY, DstW, DstH, MaskDC, MaskX, MaskY, SrcW, SrcH, SrcAnd);
|
||
StretchBlt(DstDC, DstX, DstY, DstW, DstH, MemDC, 0, 0, SrcW, SrcH, SrcInvert);
|
||
SetTextColor(DstDC, crText);
|
||
SetBkColor(DstDC, crBack);
|
||
|
||
if Save <> 0 then SelectObject(MemDC, Save);
|
||
DeleteObject(MemBmp);
|
||
finally
|
||
if SavePal <> 0 then SelectPalette(MemDC, SavePal, False);
|
||
DeleteDC(MemDC);
|
||
end;
|
||
end;
|
||
|
||
type
|
||
PRGBTripleArray = ^TRGBTripleArray;
|
||
TRGBTripleArray = array [Byte] of TRGBTriple;
|
||
PRGBQuadArray = ^TRGBQuadArray;
|
||
TRGBQuadArray = array [Byte] of TRGBQuad;
|
||
|
||
{ RGBTripleToQuad performs in-place conversion of an OS2 color
|
||
table into a DIB color table. }
|
||
procedure RGBTripleToQuad(var ColorTable);
|
||
var
|
||
I: Integer;
|
||
P3: PRGBTripleArray;
|
||
P4: PRGBQuadArray;
|
||
begin
|
||
P3 := PRGBTripleArray(@ColorTable);
|
||
P4 := Pointer(P3);
|
||
for I := 255 downto 1 do // don't move zeroth item
|
||
with P4^[I], P3^[I] do
|
||
begin // order is significant for last item moved
|
||
rgbRed := rgbtRed;
|
||
rgbGreen := rgbtGreen;
|
||
rgbBlue := rgbtBlue;
|
||
rgbReserved := 0;
|
||
end;
|
||
P4^[0].rgbReserved := 0;
|
||
end;
|
||
|
||
{ RGBQuadToTriple performs the inverse of RGBTripleToQuad. }
|
||
procedure RGBQuadToTriple(var ColorTable; var ColorCount: Integer);
|
||
var
|
||
I: Integer;
|
||
P3: PRGBTripleArray;
|
||
P4: PRGBQuadArray;
|
||
begin
|
||
P3 := PRGBTripleArray(@ColorTable);
|
||
P4 := Pointer(P3);
|
||
for I := 1 to ColorCount-1 do // don't move zeroth item
|
||
with P4^[I], P3^[I] do
|
||
begin
|
||
rgbtRed := rgbRed;
|
||
rgbtGreen := rgbGreen;
|
||
rgbtBlue := rgbBlue;
|
||
end;
|
||
if ColorCount < 256 then
|
||
begin
|
||
FillChar(P3^[ColorCount], (256 - ColorCount) * sizeof(TRGBTriple), 0);
|
||
ColorCount := 256; // OS2 color tables always have 256 entries
|
||
end;
|
||
end;
|
||
|
||
procedure ByteSwapColors(var Colors; Count: Integer);
|
||
var // convert RGB to BGR and vice-versa. TRGBQuad <-> TPaletteEntry
|
||
SysInfo: TSystemInfo;
|
||
begin
|
||
GetSystemInfo(SysInfo);
|
||
asm
|
||
MOV EDX, Colors
|
||
MOV ECX, Count
|
||
DEC ECX
|
||
JS @@END
|
||
LEA EAX, SysInfo
|
||
CMP [EAX].TSystemInfo.wProcessorLevel, 3
|
||
JE @@386
|
||
@@1: MOV EAX, [EDX+ECX*4]
|
||
BSWAP EAX
|
||
SHR EAX,8
|
||
MOV [EDX+ECX*4],EAX
|
||
DEC ECX
|
||
JNS @@1
|
||
JMP @@END
|
||
@@386:
|
||
PUSH EBX
|
||
@@2: XOR EBX,EBX
|
||
MOV EAX, [EDX+ECX*4]
|
||
MOV BH, AL
|
||
MOV BL, AH
|
||
SHR EAX,16
|
||
SHL EBX,8
|
||
MOV BL, AL
|
||
MOV [EDX+ECX*4],EBX
|
||
DEC ECX
|
||
JNS @@2
|
||
POP EBX
|
||
@@END:
|
||
end;
|
||
end;
|
||
|
||
function CreateSystemPalette(const Entries: array of TColor): HPALETTE;
|
||
var
|
||
DC: HDC;
|
||
SysPalSize: Integer;
|
||
Pal: TMaxLogPalette;
|
||
begin
|
||
Pal.palVersion := $300;
|
||
Pal.palNumEntries := 16;
|
||
Move(Entries, Pal.palPalEntry, 16 * SizeOf(TColor));
|
||
DC := GetDC(0);
|
||
try
|
||
SysPalSize := GetDeviceCaps(DC, SIZEPALETTE);
|
||
{ Ignore the disk image of the palette for 16 color bitmaps.
|
||
Replace with the first and last 8 colors of the system palette }
|
||
if SysPalSize >= 16 then
|
||
begin
|
||
GetSystemPaletteEntries(DC, 0, 8, Pal.palPalEntry);
|
||
{ Is light and dark gray swapped? }
|
||
if TColor(Pal.palPalEntry[7]) = clSilver then
|
||
begin
|
||
GetSystemPaletteEntries(DC, SysPalSize - 8, 1, Pal.palPalEntry[7]);
|
||
GetSystemPaletteEntries(DC, SysPalSize - 7, 7, Pal.palPalEntry[Pal.palNumEntries - 7]);
|
||
GetSystemPaletteEntries(DC, 7, 1, Pal.palPalEntry[8]);
|
||
end
|
||
else
|
||
GetSystemPaletteEntries(DC, SysPalSize - 8, 8, Pal.palPalEntry[Pal.palNumEntries - 8]);
|
||
end
|
||
else
|
||
begin
|
||
end;
|
||
finally
|
||
ReleaseDC(0,DC);
|
||
end;
|
||
Result := CreatePalette(PLogPalette(@Pal)^);
|
||
end;
|
||
|
||
function SystemPaletteOverride(var Pal: TMaxLogPalette): Boolean;
|
||
var
|
||
DC: HDC;
|
||
SysPalSize: Integer;
|
||
begin
|
||
Result := False;
|
||
if SystemPalette16 <> 0 then
|
||
begin
|
||
DC := GetDC(0);
|
||
try
|
||
SysPalSize := GetDeviceCaps(DC, SIZEPALETTE);
|
||
if SysPalSize >= 16 then
|
||
begin
|
||
{ Ignore the disk image of the palette for 16 color bitmaps.
|
||
Replace with the first and last 8 colors of the system palette }
|
||
GetPaletteEntries(SystemPalette16, 0, 8, Pal.palPalEntry);
|
||
GetPaletteEntries(SystemPalette16, 8, 8, Pal.palPalEntry[Pal.palNumEntries - 8]);
|
||
Result := True;
|
||
end
|
||
finally
|
||
ReleaseDC(0,DC);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function PaletteFromDIBColorTable(DIBHandle: THandle; ColorTable: Pointer;
|
||
ColorCount: Integer): HPalette;
|
||
var
|
||
DC: HDC;
|
||
Save: THandle;
|
||
Pal: TMaxLogPalette;
|
||
begin
|
||
Result := 0;
|
||
Pal.palVersion := $300;
|
||
if DIBHandle <> 0 then
|
||
begin
|
||
DC := CreateCompatibleDC(0);
|
||
Save := SelectObject(DC, DIBHandle);
|
||
Pal.palNumEntries := GetDIBColorTable(DC, 0, 256, Pal.palPalEntry);
|
||
SelectObject(DC, Save);
|
||
DeleteDC(DC);
|
||
end
|
||
else
|
||
begin
|
||
Pal.palNumEntries := ColorCount;
|
||
Move(ColorTable^, Pal.palPalEntry, ColorCount * 4);
|
||
end;
|
||
if Pal.palNumEntries = 0 then Exit;
|
||
if (Pal.palNumEntries <> 16) or not SystemPaletteOverride(Pal) then
|
||
ByteSwapColors(Pal.palPalEntry, Pal.palNumEntries);
|
||
Result := CreatePalette(PLogPalette(@Pal)^);
|
||
end;
|
||
|
||
function PaletteToDIBColorTable(Pal: HPalette;
|
||
var ColorTable: array of TRGBQuad): Integer;
|
||
begin
|
||
Result := 0;
|
||
if (Pal = 0) or
|
||
(GetObject(Pal, sizeof(Result), @Result) = 0) or
|
||
(Result = 0) then Exit;
|
||
if Result > High(ColorTable)+1 then Result := High(ColorTable)+1;
|
||
GetPaletteEntries(Pal, 0, Result, ColorTable);
|
||
ByteSwapColors(ColorTable, Result);
|
||
end;
|
||
|
||
procedure TwoBitsFromDIB(var BI: TBitmapInfoHeader; var XorBits, AndBits: HBITMAP;
|
||
const IconSize: TPoint);
|
||
type
|
||
PLongArray = ^TLongArray;
|
||
TLongArray = array[0..1] of Longint;
|
||
var
|
||
Temp: HBITMAP;
|
||
NumColors: Integer;
|
||
DC: HDC;
|
||
Bits: Pointer;
|
||
Colors: PLongArray;
|
||
begin
|
||
with BI do
|
||
begin
|
||
biHeight := biHeight shr 1; { Size in record is doubled }
|
||
biSizeImage := BytesPerScanline(biWidth, biBitCount, 32) * biHeight;
|
||
NumColors := GetDInColors(biBitCount);
|
||
end;
|
||
DC := GetDC(0);
|
||
if DC = 0 then OutOfResources;
|
||
try
|
||
Bits := Pointer(Longint(@BI) + SizeOf(BI) + NumColors * SizeOf(TRGBQuad));
|
||
Temp := GDICheck(CreateDIBitmap(DC, BI, CBM_INIT, Bits, PBitmapInfo(@BI)^, DIB_RGB_COLORS));
|
||
try
|
||
XorBits := DupBits(Temp, IconSize, False);
|
||
finally
|
||
DeleteObject(Temp);
|
||
end;
|
||
with BI do
|
||
begin
|
||
Inc(Longint(Bits), biSizeImage);
|
||
biBitCount := 1;
|
||
biSizeImage := BytesPerScanline(biWidth, biBitCount, 32) * biHeight;
|
||
biClrUsed := 2;
|
||
biClrImportant := 2;
|
||
end;
|
||
Colors := Pointer(Longint(@BI) + SizeOf(BI));
|
||
Colors^[0] := 0;
|
||
Colors^[1] := $FFFFFF;
|
||
Temp := GDICheck(CreateDIBitmap(DC, BI, CBM_INIT, Bits, PBitmapInfo(@BI)^, DIB_RGB_COLORS));
|
||
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; const RequestedSize: TPoint; var IconSize: TPoint);
|
||
type
|
||
PIconRecArray = ^TIconRecArray;
|
||
TIconRecArray = array[0..300] of TIconRec;
|
||
var
|
||
List: PIconRecArray;
|
||
HeaderLen, Length: Integer;
|
||
BitsPerPixel: Word;
|
||
Colors, BestColor, C1, N, Index: Integer;
|
||
DC: HDC;
|
||
BI: PBitmapInfoHeader;
|
||
ResData: Pointer;
|
||
XorBits, AndBits: HBITMAP;
|
||
XorInfo, AndInfo: Windows.TBitmap;
|
||
XorMem, AndMem: Pointer;
|
||
XorLen, AndLen: Integer;
|
||
(*
|
||
var
|
||
P: PChar;
|
||
begin
|
||
P := Pointer(Integer((Stream as TCustomMemoryStream).Memory) + Stream.Position);
|
||
// N := LookupIconIdFromDirectoryEx(Pointer(P), True, 0, 0, LR_DEFAULTCOLOR);
|
||
Icon := GDICheck(CreateIconFromResourceEx(
|
||
Pointer(P + PIconRec(P)^.DIBOffset - StartOffset),
|
||
PIconRec(P)^.DIBSize, True, $00030000, 0, 0, LR_DEFAULTCOLOR));
|
||
end;
|
||
*)
|
||
|
||
function AdjustColor(I: Integer): Integer;
|
||
begin
|
||
if I = 0 then
|
||
Result := MaxInt
|
||
else
|
||
Result := I;
|
||
end;
|
||
|
||
function BetterSize(const Old, New: TIconRec): Boolean;
|
||
var
|
||
NewX, NewY, OldX, OldY: Integer;
|
||
begin
|
||
NewX := New.Width - IconSize.X;
|
||
NewY := New.Height - IconSize.Y;
|
||
OldX := Old.Width - IconSize.X;
|
||
OldY := Old.Height - IconSize.Y;
|
||
Result := (Abs(NewX) <= Abs(OldX)) and ((NewX <= 0) or (NewX <= OldX)) and
|
||
(Abs(NewY) <= Abs(OldY)) and ((NewY <= 0) or (NewY <= OldY));
|
||
end;
|
||
|
||
begin
|
||
HeaderLen := SizeOf(TIconRec) * ImageCount;
|
||
List := AllocMem(HeaderLen);
|
||
try
|
||
Stream.Read(List^, HeaderLen);
|
||
if (RequestedSize.X or RequestedSize.Y) = 0 then
|
||
begin
|
||
IconSize.X := GetSystemMetrics(SM_CXICON);
|
||
IconSize.Y := GetSystemMetrics(SM_CYICON);
|
||
end
|
||
else
|
||
IconSize := RequestedSize;
|
||
DC := GetDC(0);
|
||
if DC = 0 then OutOfResources;
|
||
try
|
||
BitsPerPixel := GetDeviceCaps(DC, PLANES) * GetDeviceCaps(DC, BITSPIXEL);
|
||
if BitsPerPixel > 8 then
|
||
Colors := MaxInt
|
||
else
|
||
Colors := 1 shl BitsPerPixel;
|
||
finally
|
||
ReleaseDC(0, DC);
|
||
end;
|
||
|
||
{ Find the image that most closely matches (<=) the current screen color
|
||
depth and the requested image size. }
|
||
Index := 0;
|
||
BestColor := AdjustColor(List^[0].Colors);
|
||
for N := 1 to ImageCount-1 do
|
||
begin
|
||
C1 := AdjustColor(List^[N].Colors);
|
||
if (C1 <= Colors) and (C1 >= BestColor) and
|
||
BetterSize(List^[Index], List^[N]) then
|
||
begin
|
||
Index := N;
|
||
BestColor := C1;
|
||
end;
|
||
end;
|
||
|
||
{ 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;
|
||
if (IconSize.X = List^[N].Width) and (IconSize.Y = List^[N].Height) then
|
||
Break; // exact match on size and color
|
||
end
|
||
else if Index = -1 then
|
||
begin // take the first icon with fewer colors than screen
|
||
if C1 <= Colors then
|
||
begin
|
||
Index := N;
|
||
C2 := C1;
|
||
end;
|
||
end
|
||
else if C1 > C2 then // take icon with more colors than first match
|
||
Index := N;
|
||
end;
|
||
if Index = -1 then Index := 0;
|
||
*)
|
||
with List^[Index] do
|
||
begin
|
||
IconSize.X := Width;
|
||
IconSize.Y := Height;
|
||
BI := AllocMem(DIBSize);
|
||
try
|
||
Stream.Seek(DIBOffset - (HeaderLen + StartOffset), 1);
|
||
Stream.Read(BI^, DIBSize);
|
||
TwoBitsFromDIB(BI^, XorBits, AndBits, IconSize);
|
||
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 GDIError;
|
||
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
|
||
DS: TDIBSection;
|
||
Bytes: Integer;
|
||
begin
|
||
DS.dsbmih.biSize := 0;
|
||
Bytes := GetObject(Bitmap, SizeOf(DS), @DS);
|
||
if Bytes = 0 then InvalidBitmap
|
||
else if (Bytes >= (sizeof(DS.dsbm) + sizeof(DS.dsbmih))) and
|
||
(DS.dsbmih.biSize >= DWORD(sizeof(DS.dsbmih))) then
|
||
BI := DS.dsbmih
|
||
else
|
||
begin
|
||
FillChar(BI, sizeof(BI), 0);
|
||
with BI, DS.dsbm do
|
||
begin
|
||
biSize := SizeOf(BI);
|
||
biWidth := bmWidth;
|
||
biHeight := bmHeight;
|
||
end;
|
||
end;
|
||
case Colors of
|
||
2: BI.biBitCount := 1;
|
||
3..16:
|
||
begin
|
||
BI.biBitCount := 4;
|
||
BI.biClrUsed := Colors;
|
||
end;
|
||
17..256:
|
||
begin
|
||
BI.biBitCount := 8;
|
||
BI.biClrUsed := Colors;
|
||
end;
|
||
else
|
||
BI.biBitCount := DS.dsbm.bmBitsPixel * DS.dsbm.bmPlanes;
|
||
end;
|
||
BI.biPlanes := 1;
|
||
if BI.biClrImportant > BI.biClrUsed then
|
||
BI.biClrImportant := BI.biClrUsed;
|
||
if BI.biSizeImage = 0 then
|
||
BI.biSizeImage := BytesPerScanLine(BI.biWidth, BI.biBitCount, 32) * Abs(BI.biHeight);
|
||
end;
|
||
|
||
procedure InternalGetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: DWORD;
|
||
var ImageSize: DWORD; Colors: Integer);
|
||
var
|
||
BI: TBitmapInfoHeader;
|
||
begin
|
||
InitializeBitmapInfoHeader(Bitmap, BI, Colors);
|
||
if BI.biBitCount > 8 then
|
||
begin
|
||
InfoHeaderSize := SizeOf(TBitmapInfoHeader);
|
||
if (BI.biCompression and BI_BITFIELDS) <> 0 then
|
||
Inc(InfoHeaderSize, 12);
|
||
end
|
||
else
|
||
if BI.biClrUsed = 0 then
|
||
InfoHeaderSize := SizeOf(TBitmapInfoHeader) +
|
||
SizeOf(TRGBQuad) * (1 shl BI.biBitCount)
|
||
else
|
||
InfoHeaderSize := SizeOf(TBitmapInfoHeader) +
|
||
SizeOf(TRGBQuad) * BI.biClrUsed;
|
||
ImageSize := BI.biSizeImage;
|
||
end;
|
||
|
||
procedure GetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: DWORD;
|
||
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;
|
||
DC: HDC;
|
||
begin
|
||
InitializeBitmapInfoHeader(Bitmap, TBitmapInfoHeader(BitmapInfo), Colors);
|
||
OldPal := 0;
|
||
DC := CreateCompatibleDC(0);
|
||
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);
|
||
DeleteDC(DC);
|
||
end;
|
||
end;
|
||
|
||
function GetDIB(Bitmap: HBITMAP; Palette: HPALETTE; var BitmapInfo; var Bits): Boolean;
|
||
begin
|
||
Result := InternalGetDIB(Bitmap, Palette, BitmapInfo, Bits, 0);
|
||
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: DWORD;
|
||
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 // This stub is required for C++ compatibility.
|
||
inherited Create; // C++ doesn't support abstract virtual constructors.
|
||
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 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 := (MyImage.Size = GraphicsImage.Size) and
|
||
CompareMem(MyImage.Memory, GraphicsImage.Memory, MyImage.Size);
|
||
finally
|
||
GraphicsImage.Free;
|
||
end;
|
||
finally
|
||
MyImage.Free;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
function TGraphic.GetPalette: HPALETTE;
|
||
begin
|
||
Result := 0;
|
||
end;
|
||
|
||
function TGraphic.GetTransparent: Boolean;
|
||
begin
|
||
Result := FTransparent;
|
||
end;
|
||
|
||
procedure TGraphic.LoadFromFile(const Filename: string);
|
||
var
|
||
Stream: TStream;
|
||
begin
|
||
Stream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
|
||
try
|
||
LoadFromStream(Stream);
|
||
finally
|
||
Stream.Free;
|
||
end;
|
||
end;
|
||
|
||
procedure TGraphic.Progress(Sender: TObject; Stage: TProgressStage;
|
||
PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string);
|
||
begin
|
||
if Assigned(FOnProgress) then
|
||
FOnProgress(Sender, Stage, PercentDone, RedrawNow, R, Msg);
|
||
end;
|
||
|
||
procedure TGraphic.ReadData(Stream: TStream);
|
||
begin
|
||
LoadFromStream(Stream);
|
||
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.SetPalette(Value: HPalette);
|
||
begin
|
||
end;
|
||
|
||
procedure TGraphic.SetModified(Value: Boolean);
|
||
begin
|
||
if Value then
|
||
Changed(Self) else
|
||
FModified := False;
|
||
end;
|
||
|
||
procedure TGraphic.SetTransparent(Value: Boolean);
|
||
begin
|
||
if Value <> FTransparent then
|
||
begin
|
||
FTransparent := Value;
|
||
Changed(Self);
|
||
end;
|
||
end;
|
||
|
||
procedure TGraphic.WriteData(Stream: TStream);
|
||
begin
|
||
SaveToStream(Stream);
|
||
end;
|
||
|
||
{ TPicture }
|
||
|
||
type
|
||
PFileFormat = ^TFileFormat;
|
||
TFileFormat = record
|
||
GraphicClass: TGraphicClass;
|
||
Extension: string;
|
||
Description: string;
|
||
DescResID: Integer;
|
||
end;
|
||
|
||
TFileFormatsList = class(TList)
|
||
public
|
||
constructor Create;
|
||
destructor Destroy; override;
|
||
procedure Add(const Ext, Desc: String; DescID: Integer; AClass: TGraphicClass);
|
||
function FindExt(Ext: string): TGraphicClass;
|
||
function FindClassName(const Classname: string): TGraphicClass;
|
||
procedure Remove(AClass: TGraphicClass);
|
||
procedure BuildFilterStrings(GraphicClass: TGraphicClass;
|
||
var Descriptions, Filters: string);
|
||
end;
|
||
|
||
constructor TFileFormatsList.Create;
|
||
begin
|
||
inherited Create;
|
||
Add('wmf', SVMetafiles, 0, TMetafile);
|
||
Add('emf', SVEnhMetafiles, 0, TMetafile);
|
||
Add('ico', SVIcons, 0, TIcon);
|
||
Add('bmp', SVBitmaps, 0, TBitmap);
|
||
end;
|
||
|
||
destructor TFileFormatsList.Destroy;
|
||
var
|
||
I: Integer;
|
||
begin
|
||
for I := 0 to Count-1 do
|
||
Dispose(PFileFormat(Items[I]));
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TFileFormatsList.Add(const Ext, Desc: String; DescID: Integer;
|
||
AClass: TGraphicClass);
|
||
var
|
||
NewRec: PFileFormat;
|
||
begin
|
||
New(NewRec);
|
||
with NewRec^ do
|
||
begin
|
||
Extension := AnsiLowerCase(Ext);
|
||
GraphicClass := AClass;
|
||
Description := Desc;
|
||
DescResID := DescID;
|
||
end;
|
||
inherited Add(NewRec);
|
||
end;
|
||
|
||
function TFileFormatsList.FindExt(Ext: string): TGraphicClass;
|
||
var
|
||
I: Integer;
|
||
begin
|
||
Ext := AnsiLowerCase(Ext);
|
||
for I := Count-1 downto 0 do
|
||
with PFileFormat(Items[I])^ do
|
||
if Extension = Ext then
|
||
begin
|
||
Result := GraphicClass;
|
||
Exit;
|
||
end;
|
||
Result := nil;
|
||
end;
|
||
|
||
function TFileFormatsList.FindClassName(const ClassName: string): TGraphicClass;
|
||
var
|
||
I: Integer;
|
||
begin
|
||
for I := Count-1 downto 0 do
|
||
begin
|
||
Result := PFileFormat(Items[I])^.GraphicClass;
|
||
if Result.ClassName = Classname then Exit;
|
||
end;
|
||
Result := nil;
|
||
end;
|
||
|
||
procedure TFileFormatsList.Remove(AClass: TGraphicClass);
|
||
var
|
||
I: Integer;
|
||
P: PFileFormat;
|
||
begin
|
||
for I := Count-1 downto 0 do
|
||
begin
|
||
P := PFileFormat(Items[I]);
|
||
if P^.GraphicClass.InheritsFrom(AClass) then
|
||
begin
|
||
Dispose(P);
|
||
Delete(I);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TFileFormatsList.BuildFilterStrings(GraphicClass: TGraphicClass;
|
||
var Descriptions, Filters: string);
|
||
var
|
||
C, I: Integer;
|
||
P: PFileFormat;
|
||
begin
|
||
Descriptions := '';
|
||
Filters := '';
|
||
C := 0;
|
||
for I := Count-1 downto 0 do
|
||
begin
|
||
P := PFileFormat(Items[I]);
|
||
if P^.GraphicClass.InheritsFrom(GraphicClass) and (P^.Extension <> '') then
|
||
with P^ do
|
||
begin
|
||
if C <> 0 then
|
||
begin
|
||
Descriptions := Descriptions + '|';
|
||
Filters := Filters + ';';
|
||
end;
|
||
if (Description = '') and (DescResID <> 0) then
|
||
Description := LoadStr(DescResID);
|
||
FmtStr(Descriptions, '%s%s (*.%s)|*.%2:s', [Descriptions, Description, Extension]);
|
||
FmtStr(Filters, '%s*.%s', [Filters, Extension]);
|
||
Inc(C);
|
||
end;
|
||
end;
|
||
if C > 1 then
|
||
FmtStr(Descriptions, '%s (%s)|%1:s|%s', [sAllFilter, Filters, Descriptions]);
|
||
end;
|
||
|
||
type
|
||
TClipboardFormats = class
|
||
private
|
||
FClasses: TList;
|
||
FFormats: TList;
|
||
public
|
||
constructor Create;
|
||
destructor Destroy; override;
|
||
procedure Add(Fmt: Word; AClass: TGraphicClass);
|
||
function FindFormat(Fmt: Word): TGraphicClass;
|
||
procedure Remove(AClass: TGraphicClass);
|
||
end;
|
||
|
||
constructor TClipboardFormats.Create;
|
||
begin
|
||
FClasses := TList.Create;
|
||
FFormats := TList.Create;
|
||
Add(CF_METAFILEPICT, TMetafile);
|
||
Add(CF_ENHMETAFILE, TMetafile);
|
||
Add(CF_BITMAP, TBitmap);
|
||
end;
|
||
|
||
destructor TClipboardFormats.Destroy;
|
||
begin
|
||
FClasses.Free;
|
||
FFormats.Free;
|
||
end;
|
||
|
||
procedure TClipboardFormats.Add(Fmt: Word; AClass: TGraphicClass);
|
||
var
|
||
I: Integer;
|
||
begin
|
||
I := FClasses.Add(AClass);
|
||
try
|
||
FFormats.Add(Pointer(Integer(Fmt)));
|
||
except
|
||
FClasses.Delete(I);
|
||
raise;
|
||
end;
|
||
end;
|
||
|
||
function TClipboardFormats.FindFormat(Fmt: Word): TGraphicClass;
|
||
var
|
||
I: Integer;
|
||
begin
|
||
for I := FFormats.Count-1 downto 0 do
|
||
if Word(FFormats[I]) = Fmt then
|
||
begin
|
||
Result := FClasses[I];
|
||
Exit;
|
||
end;
|
||
Result := nil;
|
||
end;
|
||
|
||
procedure TClipboardFormats.Remove(AClass: TGraphicClass);
|
||
var
|
||
I: Integer;
|
||
begin
|
||
for I := FClasses.Count-1 downto 0 do
|
||
if TGraphicClass(FClasses[I]).InheritsFrom(AClass) then
|
||
begin
|
||
FClasses.Delete(I);
|
||
FFormats.Delete(I);
|
||
end;
|
||
end;
|
||
|
||
var
|
||
ClipboardFormats: TClipboardFormats = nil;
|
||
FileFormats: TFileFormatsList = nil;
|
||
|
||
function GetFileFormats: TFileFormatsList;
|
||
begin
|
||
if FileFormats = nil then FileFormats := TFileFormatsList.Create;
|
||
Result := FileFormats;
|
||
end;
|
||
|
||
function GetClipboardFormats: TClipboardFormats;
|
||
begin
|
||
if ClipboardFormats = nil then ClipboardFormats := TClipboardFormats.Create;
|
||
Result := ClipboardFormats;
|
||
end;
|
||
|
||
constructor TPicture.Create;
|
||
begin
|
||
inherited Create;
|
||
GetFileFormats;
|
||
GetClipboardFormats;
|
||
end;
|
||
|
||
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;
|
||
FGraphic.OnProgress := Progress;
|
||
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;
|
||
NewGraphic.OnProgress := Progress;
|
||
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;
|
||
NewGraphic: TGraphic;
|
||
GraphicClass: TGraphicClass;
|
||
begin
|
||
Ext := ExtractFileExt(Filename);
|
||
Delete(Ext, 1, 1);
|
||
GraphicClass := FileFormats.FindExt(Ext);
|
||
if GraphicClass = nil then
|
||
raise EInvalidGraphic.CreateFmt(SUnknownExtension, [Ext]);
|
||
|
||
NewGraphic := GraphicClass.Create;
|
||
try
|
||
NewGraphic.OnProgress := Progress;
|
||
NewGraphic.LoadFromFile(Filename);
|
||
except
|
||
NewGraphic.Free;
|
||
raise;
|
||
end;
|
||
FGraphic.Free;
|
||
FGraphic := NewGraphic;
|
||
FGraphic.OnChange := Changed;
|
||
Changed(Self);
|
||
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;
|
||
GraphicClass: TGraphicClass;
|
||
begin
|
||
GraphicClass := ClipboardFormats.FindFormat(AFormat);
|
||
if GraphicClass = nil then
|
||
InvalidGraphic(@SUnknownClipboardFormat);
|
||
|
||
NewGraphic := GraphicClass.Create;
|
||
try
|
||
NewGraphic.OnProgress := Progress;
|
||
NewGraphic.LoadFromClipboardFormat(AFormat, AData, APalette);
|
||
except
|
||
NewGraphic.Free;
|
||
raise;
|
||
end;
|
||
FGraphic.Free;
|
||
FGraphic := NewGraphic;
|
||
FGraphic.OnChange := Changed;
|
||
Changed(Self);
|
||
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;
|
||
begin
|
||
Result := GetClipboardFormats.FindFormat(AFormat) <> nil;
|
||
end;
|
||
|
||
procedure TPicture.LoadFromStream(Stream: TStream);
|
||
begin
|
||
Bitmap.LoadFromStream(Stream);
|
||
end;
|
||
|
||
procedure TPicture.SaveToStream(Stream: TStream);
|
||
begin
|
||
Bitmap.SaveToStream(Stream);
|
||
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;
|
||
|
||
class procedure TPicture.RegisterFileFormat(const AExtension,
|
||
ADescription: string; AGraphicClass: TGraphicClass);
|
||
begin
|
||
GetFileFormats.Add(AExtension, ADescription, 0, AGraphicClass);
|
||
end;
|
||
|
||
class procedure TPicture.RegisterFileFormatRes(const AExtension: String;
|
||
ADescriptionResID: Integer; AGraphicClass: TGraphicClass);
|
||
begin
|
||
GetFileFormats.Add(AExtension, '', ADescriptionResID, AGraphicClass);
|
||
end;
|
||
|
||
class procedure TPicture.RegisterClipboardFormat(AFormat: Word;
|
||
AGraphicClass: TGraphicClass);
|
||
begin
|
||
GetClipboardFormats.Add(AFormat, AGraphicClass);
|
||
end;
|
||
|
||
class procedure TPicture.UnRegisterGraphicClass(AClass: TGraphicClass);
|
||
begin
|
||
if FileFormats <> nil then FileFormats.Remove(AClass);
|
||
if ClipboardFormats <> nil then ClipboardFormats.Remove(AClass);
|
||
end;
|
||
|
||
procedure TPicture.Changed(Sender: TObject);
|
||
begin
|
||
if Assigned(FOnChange) then FOnChange(Self);
|
||
if FNotify <> nil then FNotify.Changed;
|
||
end;
|
||
|
||
procedure TPicture.Progress(Sender: TObject; Stage: TProgressStage;
|
||
PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string);
|
||
begin
|
||
if Assigned(FOnProgress) then FOnProgress(Sender, Stage, PercentDone, RedrawNow, R, Msg);
|
||
end;
|
||
|
||
procedure TPicture.ReadData(Stream: TStream);
|
||
var
|
||
CName: string[63];
|
||
NewGraphic: TGraphic;
|
||
GraphicClass: TGraphicClass;
|
||
begin
|
||
Stream.Read(CName[0], 1);
|
||
Stream.Read(CName[1], Integer(CName[0]));
|
||
GraphicClass := FileFormats.FindClassName(CName);
|
||
NewGraphic := nil;
|
||
if GraphicClass <> nil then
|
||
begin
|
||
NewGraphic := GraphicClass.Create;
|
||
try
|
||
NewGraphic.ReadData(Stream);
|
||
except
|
||
NewGraphic.Free;
|
||
raise;
|
||
end;
|
||
end;
|
||
FGraphic.Free;
|
||
FGraphic := NewGraphic;
|
||
if NewGraphic <> nil then
|
||
begin
|
||
NewGraphic.OnChange := Changed;
|
||
NewGraphic.OnProgress := Progress;
|
||
end;
|
||
Changed(Self);
|
||
end;
|
||
|
||
procedure TPicture.WriteData(Stream: TStream);
|
||
var
|
||
CName: string[63];
|
||
begin
|
||
with Stream do
|
||
begin
|
||
if Graphic <> nil then
|
||
CName := Graphic.ClassName else
|
||
CName := '';
|
||
Write(CName, Length(CName) + 1);
|
||
if Graphic <> nil then
|
||
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 }
|
||
|
||
destructor TMetafileImage.Destroy;
|
||
begin
|
||
if FHandle <> 0 then DeleteEnhMetafile(FHandle);
|
||
InternalDeletePalette(FPalette);
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TMetafileImage.FreeHandle;
|
||
begin
|
||
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 GDIError;
|
||
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;
|
||
FTransparent := True;
|
||
Assign(nil);
|
||
end;
|
||
|
||
destructor TMetafile.Destroy;
|
||
begin
|
||
FImage.Release;
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TMetafile.Assign(Source: TPersistent);
|
||
var
|
||
Pal: HPalette;
|
||
begin
|
||
if (Source = nil) or (Source is TMetafile) then
|
||
begin
|
||
Pal := 0;
|
||
if FImage <> nil then
|
||
begin
|
||
Pal := FImage.FPalette;
|
||
FImage.Release;
|
||
end;
|
||
if Assigned(Source) then
|
||
begin
|
||
FImage := TMetafile(Source).FImage;
|
||
FEnhanced := TMetafile(Source).Enhanced;
|
||
end
|
||
else
|
||
begin
|
||
FImage := TMetafileImage.Create;
|
||
FEnhanced := True;
|
||
end;
|
||
FImage.Reference;
|
||
PaletteModified := (Pal <> Palette) and (Palette <> 0);
|
||
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))+1);
|
||
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.HandleAllocated: Boolean;
|
||
begin
|
||
Result := Assigned(FImage) and (FImage.FHandle <> 0);
|
||
end;
|
||
|
||
const
|
||
HundredthMMPerInch = 2540;
|
||
|
||
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, ScreenLogPixels, HundredthMMPerInch);
|
||
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: TMaxLogPalette;
|
||
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
|
||
else if Count > 256 then
|
||
Count := Count and $FF;
|
||
InternalDeletePalette(FImage.FPalette);
|
||
LogPal.palVersion := $300;
|
||
LogPal.palNumEntries := Count;
|
||
GetEnhMetaFilePaletteEntries(FImage.FHandle, Count, @LogPal.palPalEntry);
|
||
FImage.FPalette := CreatePalette(PLogPalette(@LogPal)^);
|
||
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, ScreenLogPixels, HundredthMMPerInch);
|
||
end;
|
||
|
||
procedure TMetafile.LoadFromStream(Stream: TStream);
|
||
begin
|
||
if TestEMF(Stream) then
|
||
ReadEMFStream(Stream)
|
||
else
|
||
ReadWMFStream(Stream, Stream.Size - Stream.Position);
|
||
PaletteModified := Palette <> 0;
|
||
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 Length <= 4 then
|
||
Assign(nil)
|
||
else
|
||
if TestEMF(Stream) then
|
||
ReadEMFStream(Stream)
|
||
else
|
||
ReadWMFStream(Stream, Length - Sizeof(Length));
|
||
PaletteModified := Palette <> 0;
|
||
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;
|
||
EMFHeader: TEnhMetaheader;
|
||
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,HundredthMMPerInch,WMF.Inch);
|
||
FHeight := MulDiv(WMF.Box.Bottom - WMF.Box.Top,HundredthMMPerInch,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;
|
||
// Get the maximum extent actually used by the metafile output
|
||
// and re-convert the wmf data using the new extents.
|
||
// This helps preserve whitespace margins in WMFs
|
||
GetEnhMetaFileHeader(FHandle, Sizeof(EMFHeader), @EMFHeader);
|
||
with MFP, EMFHeader.rclFrame do
|
||
begin
|
||
MM := MM_ANISOTROPIC;
|
||
xExt := Right;
|
||
yExt := Bottom;
|
||
hmf := 0;
|
||
end;
|
||
DeleteEnhMetafile(FHandle);
|
||
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;
|
||
try
|
||
if AnsiLowerCaseFileName(ExtractFileExt(Filename)) = '.wmf' then
|
||
Enhanced := False; { For 16 bit compatibility }
|
||
inherited SaveToFile(Filename);
|
||
finally
|
||
Enhanced := SaveEnh;
|
||
end;
|
||
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);
|
||
InternalDeletePalette(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;
|
||
PaletteModified := Palette <> 0;
|
||
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, HundredthMMPerInch, ScreenLogPixels);
|
||
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.SetTransparent(Value: Boolean);
|
||
begin
|
||
// Ignore assignments to this property.
|
||
// Metafiles must always be considered transparent.
|
||
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, HundredthMMPerInch, ScreenLogPixels);
|
||
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);
|
||
if Length = 0 then Exit;
|
||
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: UINT;
|
||
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 := 96 { WMF defaults to 96 units per inch }
|
||
else
|
||
Inch := FInch;
|
||
with Box do
|
||
begin
|
||
Right := MulDiv(FWidth, WMF.Inch, HundredthMMPerInch);
|
||
Bottom := MulDiv(FHeight, WMF.Inch, HundredthMMPerInch);
|
||
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 GDIError;
|
||
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;
|
||
PaletteModified := Palette <> 0;
|
||
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;
|
||
|
||
function TMetafile.ReleaseHandle: HENHMETAFILE;
|
||
begin
|
||
UniqueImage;
|
||
Result := FImage.FHandle;
|
||
FImage.FHandle := 0;
|
||
end;
|
||
|
||
var
|
||
BitmapCanvasList: TThreadList = 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;
|
||
|
||
{ FreeMemoryContexts is called by the VCL main winproc to release
|
||
memory DCs after every message is processed (garbage collection).
|
||
Only memory DCs not locked by other threads will be freed.
|
||
}
|
||
procedure FreeMemoryContexts;
|
||
var
|
||
I: Integer;
|
||
begin
|
||
with BitmapCanvasList.LockList do
|
||
try
|
||
for I := Count-1 downto 0 do
|
||
with TBitmapCanvas(Items[I]) do
|
||
if TryLock then
|
||
try
|
||
FreeContext;
|
||
finally
|
||
Unlock;
|
||
end;
|
||
finally
|
||
BitmapCanvasList.UnlockList;
|
||
end;
|
||
end;
|
||
|
||
{ DeselectBitmap is called to ensure that a bitmap handle is not
|
||
selected into any memory DC anywhere in the system. If the bitmap
|
||
handle is in use by a locked canvas, DeselectBitmap must wait for
|
||
the canvas to unlock. }
|
||
|
||
procedure DeselectBitmap(AHandle: HBITMAP);
|
||
var
|
||
I: Integer;
|
||
begin
|
||
if AHandle = 0 then Exit;
|
||
with BitmapCanvasList.LockList do
|
||
try
|
||
for I := Count - 1 downto 0 do
|
||
with TBitmapCanvas(Items[I]) do
|
||
if (FBitmap <> nil) and (FBitmap.FImage.FHandle = AHandle) then
|
||
FreeContext;
|
||
finally
|
||
BitmapCanvasList.UnlockList;
|
||
end;
|
||
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
|
||
Lock;
|
||
try
|
||
if FOldBitmap <> 0 then SelectObject(FHandle, FOldBitmap);
|
||
if FOldPalette <> 0 then SelectPalette(FHandle, FOldPalette, True);
|
||
H := FHandle;
|
||
Handle := 0;
|
||
DeleteDC(H);
|
||
BitmapCanvasList.Remove(Self);
|
||
finally
|
||
Unlock;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmapCanvas.CreateHandle;
|
||
var
|
||
H: HBITMAP;
|
||
begin
|
||
if FBitmap <> nil then
|
||
begin
|
||
Lock;
|
||
try
|
||
FBitmap.HandleNeeded;
|
||
DeselectBitmap(FBitmap.FImage.FHandle);
|
||
//!! DeselectBitmap(FBitmap.FImage.FMaskHandle);
|
||
FBitmap.PaletteNeeded;
|
||
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);
|
||
finally
|
||
Unlock;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
{ TSharedImage }
|
||
|
||
procedure TSharedImage.Reference;
|
||
begin
|
||
Inc(FRefCount);
|
||
end;
|
||
|
||
procedure TSharedImage.Release;
|
||
begin
|
||
if Pointer(Self) <> nil then
|
||
begin
|
||
Dec(FRefCount);
|
||
if FRefCount = 0 then
|
||
begin
|
||
FreeHandle;
|
||
Free;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
{ TBitmapImage }
|
||
|
||
destructor TBitmapImage.Destroy;
|
||
begin
|
||
if FDIBHandle <> 0 then
|
||
begin
|
||
DeselectBitmap(FDIBHandle);
|
||
DeleteObject(FDIBHandle);
|
||
FDIBHandle := 0;
|
||
end;
|
||
FreeHandle;
|
||
if FDIB.dshSection <> 0 then CloseHandle(FDIB.dshSection);
|
||
FreeAndNil(FSaveStream);
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TBitmapImage.FreeHandle;
|
||
begin
|
||
if (FHandle <> 0) and (FHandle <> FDIBHandle) then
|
||
begin
|
||
DeselectBitmap(FHandle);
|
||
DeleteObject(FHandle);
|
||
end;
|
||
if FMaskHandle <> 0 then
|
||
begin
|
||
DeselectBitmap(FMaskHandle);
|
||
DeleteObject(FMaskHandle);
|
||
FMaskHandle := 0;
|
||
end;
|
||
InternalDeletePalette(FPalette);
|
||
FHandle := 0;
|
||
FPalette := 0;
|
||
end;
|
||
|
||
{ TBitmap }
|
||
|
||
const
|
||
{ Mapping from color in DIB to system color }
|
||
Grays: array[0..3] of TColor = (clWhite, clSilver, clGray, clBlack);
|
||
SysGrays: array[0..3] of TColor = (clBtnHighlight, clBtnFace, clBtnShadow,
|
||
clBtnText);
|
||
|
||
{ This function will replace OldColors in Handle's colortable with NewColors and
|
||
return a new DDB which uses that color table. For bitmap's with more than
|
||
256 colors (8bpp) this function returns the original bitmap. }
|
||
function CreateMappedBmp(Handle: HBITMAP; const OldColors, NewColors: array of TColor): HBITMAP;
|
||
var
|
||
Bitmap: PBitmapInfoHeader;
|
||
ColorCount: Integer;
|
||
BitmapInfoSize: DWORD;
|
||
BitmapBitsSize: DWORD;
|
||
Bits: Pointer;
|
||
Colors: PRGBQuadArray;
|
||
I, J: Integer;
|
||
OldColor, NewColor: Integer;
|
||
ScreenDC, DC: HDC;
|
||
Save: HBITMAP;
|
||
begin
|
||
Result := Handle;
|
||
if Handle = 0 then Exit;
|
||
InternalGetDIBSizes(Handle, BitmapInfoSize, BitmapBitsSize, 0);
|
||
Bitmap := AllocMem(DWORD(BitmapInfoSize) + BitmapBitsSize);
|
||
try
|
||
Bits := Pointer(DWORD(Bitmap) + BitmapInfoSize);
|
||
InternalGetDIB(Handle, 0, Bitmap^, Bits^, 0);
|
||
if Bitmap^.biBitCount <= 8 then
|
||
begin
|
||
ColorCount := 1 shl (Bitmap^.biBitCount);
|
||
Colors := Pointer(DWORD(Bitmap) + Bitmap^.biSize);
|
||
ByteSwapColors(Colors^, ColorCount);
|
||
for I := 0 to ColorCount - 1 do
|
||
for J := Low(OldColors) to High(OldColors) do
|
||
begin
|
||
OldColor := ColorToRGB(OldColors[J]);
|
||
if Integer(Colors[I]) = OldColor then
|
||
begin
|
||
NewColor := ColorToRGB(NewColors[J]);
|
||
Integer(Colors[I]) := NewColor;
|
||
end;
|
||
end;
|
||
ByteSwapColors(Colors^, ColorCount);
|
||
ScreenDC := GetDC(0);
|
||
try
|
||
DC := CreateCompatibleDC(ScreenDC);
|
||
if DC <> 0 then
|
||
with Bitmap^ do
|
||
begin
|
||
Result := CreateCompatibleBitmap(ScreenDC, biWidth, biHeight);
|
||
if Result <> 0 then
|
||
begin
|
||
Save := SelectObject(DC, Result);
|
||
StretchDIBits(DC, 0, 0, biWidth, biHeight, 0, 0, biWidth, biHeight,
|
||
Bits, PBitmapInfo(Bitmap)^, DIB_RGB_COLORS, SrcCopy);
|
||
SelectObject(DC, Save);
|
||
end;
|
||
end;
|
||
DeleteDC(DC);
|
||
finally
|
||
ReleaseDC(0, ScreenDC);
|
||
end;
|
||
end;
|
||
finally
|
||
FreeMem(Bitmap, BitmapInfoSize + BitmapBitsSize);
|
||
end;
|
||
end;
|
||
|
||
{ This function will create a new DDB from the bitmap resource, replacing
|
||
OldColors in the colortable with NewColors. If the bitmap resource has more
|
||
than 256 colors (8bpp) this function returns the new DDB without color
|
||
modifications. }
|
||
function CreateMappedRes(Instance: THandle; ResName: PChar;
|
||
const OldColors, NewColors: array of TColor): HBITMAP;
|
||
var
|
||
Rsrc: HRSRC;
|
||
Res: THandle;
|
||
ColorCount: DWORD;
|
||
BitmapInfoSize: Integer;
|
||
Bitmap: PBitmapInfoHeader;
|
||
BitmapInfo: PBitmapInfoHeader;
|
||
Colors: PRGBQuadArray;
|
||
I, J: Integer;
|
||
OldColor, NewColor: Integer;
|
||
Bits: Pointer;
|
||
ScreenDC, DC: HDC;
|
||
Save: HBITMAP;
|
||
Temp: TBitmap;
|
||
begin
|
||
Result := 0;
|
||
Rsrc := FindResource(Instance, ResName, RT_BITMAP);
|
||
if Rsrc = 0 then Exit;
|
||
Res := LoadResource(Instance, Rsrc);
|
||
try
|
||
{ Lock the bitmap and get a pointer to the color table. }
|
||
Bitmap := LockResource(Res);
|
||
if Bitmap <> nil then
|
||
try
|
||
if (Bitmap^.biBitCount * Bitmap^.biPlanes) <= 8 then
|
||
begin
|
||
ColorCount := 1 shl (Bitmap^.biBitCount);
|
||
BitmapInfoSize := Bitmap^.biSize + ColorCount * SizeOf(TRGBQuad);
|
||
GetMem(BitmapInfo, BitmapInfoSize);
|
||
try
|
||
Move(Bitmap^, BitmapInfo^, BitmapInfoSize);
|
||
if Bitmap^.biBitCount <= 8 then
|
||
begin
|
||
Colors := Pointer(DWORD(BitmapInfo) + BitmapInfo^.biSize);
|
||
ByteSwapColors(Colors^, ColorCount);
|
||
for I := 0 to ColorCount - 1 do
|
||
for J := Low(OldColors) to High(OldColors) do
|
||
begin
|
||
OldColor := ColorToRGB(OldColors[J]);
|
||
if Integer(Colors[I]) = OldColor then
|
||
begin
|
||
NewColor := ColorToRGB(NewColors[J]);
|
||
Integer(Colors[I]) := NewColor;
|
||
end;
|
||
end;
|
||
ByteSwapColors(Colors^, ColorCount);
|
||
end;
|
||
{ First skip over the header structure and color table entries, if any. }
|
||
Bits := Pointer(Longint(Bitmap) + BitmapInfoSize);
|
||
{ Create a color bitmap compatible with the display device. }
|
||
ScreenDC := GetDC(0);
|
||
try
|
||
DC := CreateCompatibleDC(ScreenDC);
|
||
if DC <> 0 then
|
||
with BitmapInfo^ do
|
||
begin
|
||
Result := CreateCompatibleBitmap(ScreenDC, biWidth, biHeight);
|
||
if Result <> 0 then
|
||
begin
|
||
Save := SelectObject(DC, Result);
|
||
StretchDIBits(DC, 0, 0, biWidth, biHeight, 0, 0, biWidth, biHeight,
|
||
Bits, PBitmapInfo(BitmapInfo)^, DIB_RGB_COLORS, SrcCopy);
|
||
SelectObject(DC, Save);
|
||
end;
|
||
end;
|
||
DeleteDC(DC);
|
||
finally
|
||
ReleaseDC(0, ScreenDC);
|
||
end;
|
||
finally
|
||
FreeMem(BitmapInfo, BitmapInfoSize);
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
Temp := TBitmap.Create;
|
||
try
|
||
{$IFDEF MSWINDOWS}
|
||
Temp.LoadFromResourceID(Instance, Integer(ResName));
|
||
{$ELSE}
|
||
Temp.LoadFromResourceName(Instance, ResName);
|
||
{$ENDIF}
|
||
Result := Temp.ReleaseHandle;
|
||
finally
|
||
Temp.Free;
|
||
end;
|
||
end;
|
||
finally
|
||
UnlockResource(Res);
|
||
end;
|
||
finally
|
||
FreeResource(Res);
|
||
end;
|
||
end;
|
||
|
||
{ This function replaces the standard gray colors in a bitmap with the system
|
||
grays (Grays, SysGrays). }
|
||
function CreateGrayMappedBmp(Handle: HBITMAP): HBITMAP;
|
||
begin
|
||
Result := CreateMappedBmp(Handle, Grays, SysGrays);
|
||
end;
|
||
|
||
{ This function replaces the standard gray colors in a bitmap resource with the
|
||
system grays (Grays, SysGrays). }
|
||
function CreateGrayMappedRes(Instance: THandle; ResName: PChar): HBITMAP;
|
||
begin
|
||
Result := CreateMappedRes(Instance, ResName, Grays, SysGrays);
|
||
end;
|
||
|
||
procedure UpdateDIBColorTable(DIBHandle: HBITMAP; Pal: HPalette;
|
||
const DIB: TDIBSection);
|
||
var
|
||
ScreenDC, DC: HDC;
|
||
OldBM: HBitmap;
|
||
ColorCount: Integer;
|
||
Colors: array [Byte] of TRGBQuad;
|
||
begin
|
||
if (DIBHandle <> 0) and (DIB.dsbmih.biBitCount <= 8) then
|
||
begin
|
||
ColorCount := PaletteToDIBColorTable(Pal, Colors);
|
||
if ColorCount = 0 then Exit;
|
||
ScreenDC := GetDC(0);
|
||
DC := CreateCompatibleDC(ScreenDC);
|
||
OldBM := SelectObject(DC, DIBHandle);
|
||
try
|
||
SetDIBColorTable(DC, 0, ColorCount, Colors);
|
||
finally
|
||
SelectObject(DC, OldBM);
|
||
DeleteDC(DC);
|
||
ReleaseDC(0, ScreenDC);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure FixupBitFields(var DIB: TDIBSection);
|
||
begin
|
||
if (DIB.dsbmih.biCompression and BI_BITFIELDS <> 0) and
|
||
(DIB.dsBitFields[0] = 0) then
|
||
if DIB.dsbmih.biBitCount = 16 then
|
||
begin
|
||
// fix buggy 16 bit color drivers
|
||
DIB.dsBitFields[0] := $F800;
|
||
DIB.dsBitFields[1] := $07E0;
|
||
DIB.dsBitFields[2] := $001F;
|
||
end else if DIB.dsbmih.biBitCount = 32 then
|
||
begin
|
||
// fix buggy 32 bit color drivers
|
||
DIB.dsBitFields[0] := $00FF0000;
|
||
DIB.dsBitFields[1] := $0000FF00;
|
||
DIB.dsBitFields[2] := $000000FF;
|
||
end;
|
||
end;
|
||
|
||
function CopyBitmap(Handle: HBITMAP; OldPalette, NewPalette: HPALETTE;
|
||
var DIB: TDIBSection; Canvas: TCanvas): HBITMAP;
|
||
var
|
||
OldScr, NewScr: HBITMAP;
|
||
ScreenDC, NewImageDC, OldImageDC: HDC;
|
||
BI: PBitmapInfo;
|
||
BitsMem: Pointer;
|
||
SrcDIB: TDIBSection;
|
||
MonoColors: array [0..1] of Integer;
|
||
Pal1, Pal2: HPalette;
|
||
begin
|
||
Result := 0;
|
||
with DIB, dsbm, dsbmih do
|
||
begin
|
||
if (biSize <> 0) and ((biWidth = 0) or (biHeight = 0)) then Exit;
|
||
if (biSize = 0) and ((bmWidth = 0) or (bmHeight = 0)) then Exit;
|
||
end;
|
||
|
||
DeselectBitmap(Handle);
|
||
|
||
SrcDIB.dsbmih.biSize := 0;
|
||
if Handle <> 0 then
|
||
if GetObject(Handle, sizeof(SrcDIB), @SrcDIB) < sizeof(SrcDIB.dsbm) then
|
||
InvalidBitmap;
|
||
|
||
ScreenDC := GDICheck(GetDC(0));
|
||
NewImageDC := GDICheck(CreateCompatibleDC(ScreenDC));
|
||
with DIB.dsbm do
|
||
try
|
||
if DIB.dsbmih.biSize < DWORD(sizeof(DIB.dsbmih)) then
|
||
if (bmPlanes or bmBitsPixel) = 1 then // monochrome
|
||
Result := GDICheck(CreateBitmap(bmWidth, bmHeight, 1, 1, nil))
|
||
else // Create DDB
|
||
Result := GDICheck(CreateCompatibleBitmap(ScreenDC, bmWidth, bmHeight))
|
||
else // Create DIB
|
||
begin
|
||
GetMem(BI, sizeof(TBitmapInfo) + 256 * sizeof(TRGBQuad));
|
||
with DIB.dsbmih do
|
||
try
|
||
biSize := sizeof(BI.bmiHeader);
|
||
biPlanes := 1;
|
||
if biBitCount = 0 then
|
||
biBitCount := GetDeviceCaps(ScreenDC, BITSPIXEL) * GetDeviceCaps(ScreenDC, PLANES);
|
||
BI.bmiHeader := DIB.dsbmih;
|
||
bmWidth := biWidth;
|
||
bmHeight := biHeight;
|
||
|
||
if (biBitCount <= 8) then
|
||
begin
|
||
if (biBitCount = 1) and ((Handle = 0) or (SrcDIB.dsbm.bmBits = nil)) then
|
||
begin // set mono DIB to white/black when converting from DDB.
|
||
Integer(BI^.bmiColors[0]) := 0;
|
||
PInteger(Integer(@BI^.bmiColors) + sizeof(Integer))^ := $FFFFFF;
|
||
end
|
||
else if (NewPalette <> 0) then
|
||
PaletteToDIBColorTable(NewPalette, PRGBQuadArray(@BI.bmiColors)^)
|
||
else if Handle <> 0 then
|
||
begin
|
||
NewScr := SelectObject(NewImageDC, Handle);
|
||
if (SrcDIB.dsbmih.biSize > 0) and (SrcDIB.dsbm.bmBits <> nil) then
|
||
biClrUsed := GetDIBColorTable(NewImageDC, 0, 256, BI^.bmiColors)
|
||
else
|
||
GetDIBits(NewImageDC, Handle, 0, Abs(biHeight), nil, BI^, DIB_RGB_COLORS);
|
||
SelectObject(NewImageDC, NewScr);
|
||
end;
|
||
end
|
||
else if ((biBitCount = 16) or (biBitCount = 32)) and
|
||
((biCompression and BI_BITFIELDS) <> 0) then
|
||
begin
|
||
FixupBitFields(DIB);
|
||
Move(DIB.dsBitFields, BI.bmiColors, sizeof(DIB.dsBitFields));
|
||
end;
|
||
|
||
Result := GDICheck(CreateDIBSection(ScreenDC, BI^, DIB_RGB_COLORS, BitsMem, 0, 0));
|
||
if (BitsMem = nil) then GDIError;
|
||
|
||
if (Handle <> 0) and (SrcDIB.dsbm.bmWidth = biWidth) and
|
||
(SrcDIB.dsbm.bmHeight = biHeight) and (biBitCount > 8) then
|
||
begin // shortcut bitblt steps
|
||
GetDIBits(NewImageDC, Handle, 0, Abs(biHeight), BitsMem, BI^, DIB_RGB_COLORS);
|
||
Exit;
|
||
end;
|
||
finally
|
||
FreeMem(BI);
|
||
end;
|
||
end;
|
||
|
||
GDICheck(Result);
|
||
NewScr := GDICheck(SelectObject(NewImageDC, Result));
|
||
try
|
||
try
|
||
Pal1 := 0;
|
||
Pal2 := 0;
|
||
if NewPalette <> 0 then
|
||
begin
|
||
Pal1 := SelectPalette(NewImageDC, NewPalette, False);
|
||
RealizePalette(NewImageDC);
|
||
end;
|
||
try
|
||
if Canvas <> nil then
|
||
begin
|
||
FillRect(NewImageDC, Rect(0, 0, bmWidth, bmHeight),
|
||
Canvas.Brush.Handle);
|
||
SetTextColor(NewImageDC, ColorToRGB(Canvas.Font.Color));
|
||
SetBkColor(NewImageDC, ColorToRGB(Canvas.Brush.Color));
|
||
if (DIB.dsbmih.biBitCount = 1) and (DIB.dsbm.bmBits <> nil) then
|
||
begin
|
||
MonoColors[0] := ColorToRGB(Canvas.Font.Color);
|
||
MonoColors[1] := ColorToRGB(Canvas.Brush.Color);
|
||
SetDIBColorTable(NewImageDC, 0, 2, MonoColors);
|
||
end;
|
||
end
|
||
else
|
||
PatBlt(NewImageDC, 0, 0, bmWidth, bmHeight, WHITENESS);
|
||
if Handle <> 0 then
|
||
begin
|
||
OldImageDC := GDICheck(CreateCompatibleDC(ScreenDC));
|
||
try
|
||
OldScr := GDICheck(SelectObject(OldImageDC, Handle));
|
||
if OldPalette <> 0 then
|
||
begin
|
||
Pal2 := SelectPalette(OldImageDC, OldPalette, False);
|
||
RealizePalette(OldImageDC);
|
||
end;
|
||
if Canvas <> nil then
|
||
begin
|
||
SetTextColor(OldImageDC, ColorToRGB(Canvas.Font.Color));
|
||
SetBkColor(OldImageDC, ColorToRGB(Canvas.Brush.Color));
|
||
end;
|
||
BitBlt(NewImageDC, 0, 0, bmWidth, bmHeight, OldImageDC, 0, 0, SRCCOPY);
|
||
if OldPalette <> 0 then
|
||
SelectPalette(OldImageDC, Pal2, True);
|
||
GDICheck(SelectObject(OldImageDC, OldScr));
|
||
finally
|
||
DeleteDC(OldImageDC);
|
||
end;
|
||
end;
|
||
finally
|
||
if NewPalette <> 0 then
|
||
SelectPalette(NewImageDC, Pal1, True);
|
||
end;
|
||
finally
|
||
SelectObject(NewImageDC, NewScr);
|
||
end;
|
||
except
|
||
DeleteObject(Result);
|
||
raise;
|
||
end;
|
||
finally
|
||
DeleteDC(NewImageDC);
|
||
ReleaseDC(0, ScreenDC);
|
||
if (Result <> 0) then GetObject(Result, sizeof(DIB), @DIB);
|
||
end;
|
||
end;
|
||
|
||
function CopyPalette(Palette: HPALETTE): HPALETTE;
|
||
var
|
||
PaletteSize: Integer;
|
||
LogPal: TMaxLogPalette;
|
||
begin
|
||
Result := 0;
|
||
if Palette = 0 then Exit;
|
||
PaletteSize := 0;
|
||
if GetObject(Palette, SizeOf(PaletteSize), @PaletteSize) = 0 then Exit;
|
||
if PaletteSize = 0 then Exit;
|
||
with LogPal do
|
||
begin
|
||
palVersion := $0300;
|
||
palNumEntries := PaletteSize;
|
||
GetPaletteEntries(Palette, 0, PaletteSize, palPalEntry);
|
||
end;
|
||
Result := CreatePalette(PLogPalette(@LogPal)^);
|
||
end;
|
||
|
||
function CopyBitmapAsMask(Handle: HBITMAP; Palette: HPALETTE;
|
||
TransparentColor: TColorRef): HBITMAP;
|
||
var
|
||
DIB: TDIBSection;
|
||
ScreenDC, BitmapDC, MonoDC: HDC;
|
||
BkColor: TColorRef;
|
||
Remove: Boolean;
|
||
SaveBitmap, SaveMono: HBITMAP;
|
||
begin
|
||
Result := 0;
|
||
if (Handle <> 0) and (GetObject(Handle, SizeOf(DIB), @DIB) <> 0) then
|
||
begin
|
||
DeselectBitmap(Handle);
|
||
ScreenDC := 0;
|
||
MonoDC := 0;
|
||
try
|
||
ScreenDC := GDICheck(GetDC(0));
|
||
MonoDC := GDICheck(CreateCompatibleDC(ScreenDC));
|
||
with DIB, dsBm do
|
||
begin
|
||
Result := CreateBitmap(bmWidth, bmHeight, 1, 1, nil);
|
||
if Result <> 0 then
|
||
begin
|
||
SaveMono := SelectObject(MonoDC, Result);
|
||
if TransparentColor = TColorRef(clNone) then
|
||
PatBlt(MonoDC, 0, 0, bmWidth, bmHeight, Blackness)
|
||
else
|
||
begin
|
||
BitmapDC := GDICheck(CreateCompatibleDC(ScreenDC));
|
||
try
|
||
{ Convert DIB to DDB }
|
||
if bmBits <> nil then
|
||
begin
|
||
Remove := True;
|
||
DIB.dsbmih.biSize := 0;
|
||
Handle := CopyBitmap(Handle, Palette, Palette, DIB, nil);
|
||
end
|
||
else Remove := False;
|
||
SaveBitmap := SelectObject(BitmapDC, Handle);
|
||
if Palette <> 0 then
|
||
begin
|
||
SelectPalette(BitmapDC, Palette, False);
|
||
RealizePalette(BitmapDC);
|
||
SelectPalette(MonoDC, Palette, False);
|
||
RealizePalette(MonoDC);
|
||
end;
|
||
BkColor := SetBkColor(BitmapDC, TransparentColor);
|
||
BitBlt(MonoDC, 0, 0, bmWidth, bmHeight, BitmapDC, 0, 0, SrcCopy);
|
||
SetBkColor(BitmapDC, BkColor);
|
||
if SaveBitmap <> 0 then SelectObject(BitmapDC, SaveBitmap);
|
||
if Remove then DeleteObject(Handle);
|
||
finally
|
||
DeleteDC(BitmapDC);
|
||
end;
|
||
end;
|
||
if SaveMono <> 0 then SelectObject(MonoDC, SaveMono);
|
||
end;
|
||
end;
|
||
finally
|
||
if MonoDC <> 0 then DeleteDC(MonoDC);
|
||
if ScreenDC <> 0 then ReleaseDC(0, ScreenDC);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
constructor TBitmap.Create;
|
||
begin
|
||
inherited Create;
|
||
FTransparentColor := clDefault;
|
||
FImage := TBitmapImage.Create;
|
||
FImage.Reference;
|
||
if DDBsOnly then HandleType := bmDDB;
|
||
end;
|
||
|
||
destructor TBitmap.Destroy;
|
||
begin
|
||
FreeContext;
|
||
FImage.Release;
|
||
FCanvas.Free;
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TBitmap.Assign(Source: TPersistent);
|
||
var
|
||
DIB: TDIBSection;
|
||
begin
|
||
if (Source = nil) or (Source is TBitmap) then
|
||
begin
|
||
EnterCriticalSection(BitmapImageLock);
|
||
try
|
||
if Source <> nil then
|
||
begin
|
||
TBitmap(Source).FImage.Reference;
|
||
FImage.Release;
|
||
FImage := TBitmap(Source).FImage;
|
||
FTransparent := TBitmap(Source).FTransparent;
|
||
FTransparentColor := TBitmap(Source).FTransparentColor;
|
||
FTransparentMode := TBitmap(Source).FTransparentMode;
|
||
end
|
||
else
|
||
begin
|
||
FillChar(DIB, Sizeof(DIB), 0);
|
||
NewImage(0, 0, DIB, False);
|
||
end;
|
||
finally
|
||
LeaveCriticalSection(BitmapImageLock);
|
||
end;
|
||
PaletteModified := Palette <> 0;
|
||
Changed(Self);
|
||
end
|
||
else inherited Assign(Source);
|
||
end;
|
||
|
||
procedure TBitmap.CopyImage(AHandle: HBITMAP; APalette: HPALETTE; DIB: TDIBSection);
|
||
var
|
||
NewHandle, NewPalette: THandle;
|
||
begin
|
||
FreeContext;
|
||
NewHandle := 0;
|
||
NewPalette := 0;
|
||
try
|
||
if APalette = SystemPalette16 then
|
||
NewPalette := APalette
|
||
else
|
||
NewPalette := CopyPalette(APalette);
|
||
NewHandle := CopyBitmap(AHandle, APalette, NewPalette, DIB, FCanvas);
|
||
NewImage(NewHandle, NewPalette, DIB, FImage.FOS2Format);
|
||
except
|
||
InternalDeletePalette(NewPalette);
|
||
if NewHandle <> 0 then DeleteObject(NewHandle);
|
||
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;
|
||
FImage.FDIB.dsbmih.biClrUsed := 0;
|
||
FImage.FDIB.dsbmih.biClrImportant := 0;
|
||
FreeAndNil(FImage.FSaveStream);
|
||
end;
|
||
|
||
procedure TBitmap.Changed(Sender: TObject);
|
||
begin
|
||
FMaskBitsValid := False;
|
||
inherited Changed(Sender);
|
||
end;
|
||
|
||
procedure TBitmap.Dormant;
|
||
var
|
||
s: TMemoryStream;
|
||
DIB: TDIBSection;
|
||
begin
|
||
s := TMemoryStream.Create;
|
||
SaveToStream(s);
|
||
S.Size := S.Size; // compact to minimum buffer
|
||
DIB := FImage.FDIB;
|
||
DIB.dsbm.bmBits := nil;
|
||
FreeContext; // InternalDeletePalette requires this
|
||
FreeAndNil(FCanvas);
|
||
NewImage(0, 0, DIB, FImage.FOS2Format, s);
|
||
end;
|
||
|
||
procedure TBitmap.Draw(ACanvas: TCanvas; const Rect: TRect);
|
||
var
|
||
OldPalette: HPalette;
|
||
RestorePalette: Boolean;
|
||
DoHalftone: Boolean;
|
||
Pt: TPoint;
|
||
BPP: Integer;
|
||
MaskDC: HDC;
|
||
Save: THandle;
|
||
begin
|
||
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;
|
||
BPP := GetDeviceCaps(ACanvas.FHandle, BITSPIXEL) *
|
||
GetDeviceCaps(ACanvas.FHandle, PLANES);
|
||
DoHalftone := (BPP <= 8) and (BPP < (FDIB.dsbm.bmBitsPixel * FDIB.dsbm.bmPlanes));
|
||
if DoHalftone then
|
||
begin
|
||
GetBrushOrgEx(ACanvas.FHandle, pt);
|
||
SetStretchBltMode(ACanvas.FHandle, HALFTONE);
|
||
SetBrushOrgEx(ACanvas.FHandle, pt.x, pt.y, @pt);
|
||
end else if not Monochrome then
|
||
SetStretchBltMode(ACanvas.Handle, STRETCH_DELETESCANS);
|
||
try
|
||
{ Call MaskHandleNeeded prior to creating the canvas handle since
|
||
it causes FreeContext to be called. }
|
||
if Transparent then MaskHandleNeeded;
|
||
Canvas.RequiredState(csAllValid);
|
||
if Transparent then
|
||
begin
|
||
Save := 0;
|
||
MaskDC := 0;
|
||
try
|
||
MaskDC := GDICheck(CreateCompatibleDC(0));
|
||
Save := SelectObject(MaskDC, FMaskHandle);
|
||
TransparentStretchBlt(ACanvas.FHandle, Left, Top, Right - Left,
|
||
Bottom - Top, Canvas.FHandle, 0, 0, FDIB.dsbm.bmWidth,
|
||
FDIB.dsbm.bmHeight, MaskDC, 0, 0);
|
||
finally
|
||
if Save <> 0 then SelectObject(MaskDC, Save);
|
||
if MaskDC <> 0 then DeleteDC(MaskDC);
|
||
end;
|
||
end
|
||
else
|
||
StretchBlt(ACanvas.FHandle, Left, Top, Right - Left, Bottom - Top,
|
||
Canvas.FHandle, 0, 0, FDIB.dsbm.bmWidth,
|
||
FDIB.dsbm.bmHeight, ACanvas.CopyMode);
|
||
finally
|
||
if RestorePalette then
|
||
SelectPalette(ACanvas.FHandle, OldPalette, True);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
{ FreeImage:
|
||
If there are multiple references to the image, create a unique copy of the image.
|
||
If FHandle = FDIBHandle, the DIB memory will be updated when the drawing
|
||
handle is drawn upon, so no changes are needed to maintain image integrity.
|
||
If FHandle <> FDIBHandle, the DIB will not track with changes made to
|
||
the DDB, so destroy the DIB handle (but keep the DIB pixel format info). }
|
||
procedure TBitmap.FreeImage;
|
||
var
|
||
P: HPalette;
|
||
begin
|
||
with FImage do
|
||
if FRefCount > 1 then
|
||
begin
|
||
HandleNeeded;
|
||
if FHalftone then
|
||
P := 0
|
||
else
|
||
P := FPalette;
|
||
CopyImage(FHandle, P, FDIB)
|
||
end
|
||
else if (FHandle <> 0) and (FHandle <> FDIBHandle) then
|
||
begin
|
||
if FDIBHandle <> 0 then
|
||
if not DeleteObject(FDIBHandle) then GDIError;
|
||
FDIBHandle := 0;
|
||
FDIB.dsbm.bmBits := nil;
|
||
end;
|
||
end;
|
||
|
||
function TBitmap.GetEmpty;
|
||
begin
|
||
with FImage do
|
||
Result := (FHandle = 0) and (FDIBHandle = 0) and (FSaveStream = nil);
|
||
end;
|
||
|
||
function TBitmap.GetCanvas: TCanvas;
|
||
begin
|
||
if FCanvas = nil then
|
||
begin
|
||
HandleNeeded;
|
||
if FCanvas = nil then // possible recursion
|
||
begin
|
||
FCanvas := TBitmapCanvas.Create(Self);
|
||
FCanvas.OnChange := Changed;
|
||
FCanvas.OnChanging := Changing;
|
||
end;
|
||
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.HandleAllocated: Boolean;
|
||
begin
|
||
Result := Assigned(FImage) and (FImage.FHandle <> 0);
|
||
end;
|
||
|
||
function TBitmap.GetHandleType: TBitmapHandleType;
|
||
begin
|
||
with FImage do
|
||
begin
|
||
if (FHandle = 0) or (FHandle = FDIBHandle) then
|
||
if FDIBHandle = 0 then
|
||
if FDIB.dsbmih.biSize = 0 then
|
||
Result := bmDDB
|
||
else
|
||
Result := bmDIB
|
||
else
|
||
Result := bmDIB
|
||
else
|
||
Result := bmDDB;
|
||
end;
|
||
end;
|
||
|
||
function TBitmap.GetHeight: Integer;
|
||
begin
|
||
Result := Abs(FImage.FDIB.dsbm.bmHeight);
|
||
end;
|
||
|
||
function TBitmap.GetMaskHandle: HBITMAP;
|
||
begin
|
||
MaskHandleNeeded;
|
||
Result := FImage.FMaskHandle;
|
||
end;
|
||
|
||
function TBitmap.GetMonochrome: Boolean;
|
||
begin
|
||
with FImage.FDIB.dsbm do
|
||
Result := (bmPlanes = 1) and (bmBitsPixel = 1);
|
||
end;
|
||
|
||
function TBitmap.GetPalette: HPALETTE;
|
||
begin
|
||
PaletteNeeded;
|
||
Result := FImage.FPalette;
|
||
end;
|
||
|
||
function TBitmap.GetPixelFormat: TPixelFormat;
|
||
begin
|
||
Result := pfCustom;
|
||
if HandleType = bmDDB then
|
||
Result := pfDevice
|
||
else
|
||
with FImage.FDIB, dsbmih do
|
||
case biBitCount of
|
||
1: Result := pf1Bit;
|
||
4: Result := pf4Bit;
|
||
8: Result := pf8Bit;
|
||
16: case biCompression of
|
||
BI_RGB : Result := pf15Bit;
|
||
BI_BITFIELDS: if dsBitFields[1] = $7E0 then Result := pf16Bit;
|
||
end;
|
||
24: Result := pf24Bit;
|
||
32: if biCompression = BI_RGB then Result := pf32Bit;
|
||
end;
|
||
end;
|
||
|
||
function TBitmap.GetScanLine(Row: Integer): Pointer;
|
||
begin
|
||
Changing(Self);
|
||
with FImage.FDIB, dsbm, dsbmih do
|
||
begin
|
||
if (Row < 0) or (Row >= bmHeight) then
|
||
InvalidOperation(@SScanLine);
|
||
DIBNeeded;
|
||
GDIFlush;
|
||
if biHeight > 0 then // bottom-up DIB
|
||
Row := biHeight - Row - 1;
|
||
Integer(Result) := Integer(bmBits) +
|
||
Row * BytesPerScanline(biWidth, biBitCount, 32);
|
||
end;
|
||
end;
|
||
|
||
function TBitmap.GetTransparentColor: TColor;
|
||
begin
|
||
if FTransparentColor = clDefault then
|
||
begin
|
||
if Monochrome then
|
||
Result := clWhite
|
||
else
|
||
Result := Canvas.Pixels[0, Height - 1];
|
||
end
|
||
else Result := ColorToRGB(FTransparentColor);
|
||
Result := Result or $02000000;
|
||
end;
|
||
|
||
function TBitmap.GetWidth: Integer;
|
||
begin
|
||
Result := FImage.FDIB.dsbm.bmWidth;
|
||
end;
|
||
|
||
procedure TBitmap.DIBNeeded;
|
||
begin
|
||
with FImage do
|
||
begin
|
||
if (FHandle = 0) or (FDIBHandle <> 0) then Exit;
|
||
PaletteNeeded;
|
||
if FDIB.dsbmih.biSize = 0 then
|
||
begin
|
||
GetObject(FHandle, sizeof(FDIB), @FDIB);
|
||
with FDIB, dsbm, dsbmih do
|
||
begin
|
||
biSize := sizeof(dsbmih);
|
||
biWidth := bmWidth;
|
||
biHeight := bmHeight;
|
||
biPlanes := 1;
|
||
biBitCount := bmPlanes * bmBitsPixel;
|
||
end;
|
||
end;
|
||
FDIBHandle := CopyBitmap(FHandle, FPalette, FPalette, FDIB, nil);
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.FreeContext;
|
||
begin
|
||
if (FCanvas <> nil) then TBitmapCanvas(FCanvas).FreeContext;
|
||
end;
|
||
|
||
procedure TBitmap.HandleNeeded;
|
||
var
|
||
vChange: TNotifyEvent;
|
||
begin
|
||
if (FImage.FHandle = 0) and (FImage.FDIBHandle = 0) and (FImage.FSaveStream <> nil) then
|
||
begin
|
||
FImage.FSaveStream.Position := 0;
|
||
vChange := OnChange;
|
||
try
|
||
OnChange := nil;
|
||
LoadFromStream(FImage.FSaveStream); // Current FImage may be destroyed here
|
||
finally
|
||
OnChange := vChange;
|
||
end;
|
||
end;
|
||
|
||
with FImage do
|
||
if FHandle = 0 then
|
||
FHandle := FDIBHandle;
|
||
end;
|
||
|
||
procedure TBitmap.Mask(TransparentColor: TColor);
|
||
var
|
||
NewHandle, NewPalette: THandle;
|
||
DIB: TDIBSection;
|
||
begin
|
||
NewHandle := 0;
|
||
NewPalette := 0;
|
||
try
|
||
FreeContext;
|
||
HandleNeeded;
|
||
NewHandle := CopyBitmapAsMask(FImage.FHandle, FImage.FPalette,
|
||
ColorToRGB(TransparentColor));
|
||
FillChar(DIB, SizeOf(DIB), 0);
|
||
GetObject(NewHandle, SizeOf(DIB), @DIB);
|
||
if FImage.FPalette = SystemPalette16 then
|
||
NewPalette := FImage.FPalette
|
||
else
|
||
NewPalette := CopyPalette(FImage.FPalette);
|
||
NewImage(NewHandle, NewPalette, DIB, FImage.FOS2Format);
|
||
except
|
||
InternalDeletePalette(NewPalette);
|
||
if NewHandle <> 0 then DeleteObject(NewHandle);
|
||
raise;
|
||
end;
|
||
Changed(Self);
|
||
end;
|
||
|
||
procedure TBitmap.MaskHandleNeeded;
|
||
begin
|
||
if FMaskValid and FMaskBitsValid then Exit;
|
||
with FImage do
|
||
begin
|
||
{ Delete existing mask if any }
|
||
if FMaskHandle <> 0 then
|
||
begin
|
||
DeselectBitmap(FMaskHandle);
|
||
DeleteObject(FMaskHandle);
|
||
FMaskHandle := 0;
|
||
end;
|
||
FreeContext;
|
||
HandleNeeded; // may change FImage instance pointer
|
||
end;
|
||
with FImage do // use new FImage from here on
|
||
begin
|
||
FMaskHandle := CopyBitmapAsMask(FHandle, FPalette, GetTransparentColor);
|
||
FMaskValid := True;
|
||
FMaskBitsValid := True;
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.PaletteNeeded;
|
||
var
|
||
DC: HDC;
|
||
begin
|
||
with FImage do
|
||
begin
|
||
if FIgnorePalette or (FPalette <> 0) or (FDIBHandle = 0) then Exit;
|
||
if FHandle = FDIBHandle then DeselectBitmap(FDIBHandle);
|
||
FPalette := PaletteFromDIBColorTable(FDIBHandle, nil, 1 shl FDIB.dsbmih.biBitCount);
|
||
if FPalette <> 0 then Exit;
|
||
DC := GDICheck(GetDC(0));
|
||
FHalftone := FHalftone or
|
||
((GetDeviceCaps(DC, BITSPIXEL) * GetDeviceCaps(DC, PLANES)) <
|
||
(FDIB.dsbm.bmBitsPixel * FDIB.dsbm.bmPlanes));
|
||
if FHalftone then FPalette := CreateHalftonePalette(DC);
|
||
ReleaseDC(0, DC);
|
||
if FPalette = 0 then IgnorePalette := True;
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
|
||
APalette: HPALETTE);
|
||
var
|
||
DIB: TDIBSection;
|
||
begin
|
||
if (AFormat <> CF_BITMAP) or (AData = 0) then
|
||
InvalidGraphic(@SUnknownClipboardFormat);
|
||
FreeContext;
|
||
FillChar(DIB, sizeof(DIB), 0);
|
||
GetObject(AData, sizeof(DIB), @DIB);
|
||
if DIB.dsbm.bmBits = nil then DIB.dsbmih.biSize := 0;
|
||
CopyImage(AData, APalette, DIB);
|
||
FImage.FOS2Format := False;
|
||
PaletteModified := Palette <> 0;
|
||
Changed(Self);
|
||
end;
|
||
|
||
procedure TBitmap.LoadFromStream(Stream: TStream);
|
||
begin
|
||
ReadStream(Stream, Stream.Size - Stream.Position);
|
||
end;
|
||
|
||
procedure TBitmap.LoadFromResourceName(Instance: THandle; const ResName: string);
|
||
var
|
||
Stream: TCustomMemoryStream;
|
||
begin
|
||
Stream := TResourceStream.Create(Instance, ResName, RT_BITMAP);
|
||
try
|
||
ReadDIB(Stream, Stream.Size);
|
||
finally
|
||
Stream.Free;
|
||
end;
|
||
end;
|
||
|
||
{$IFDEF MSWINDOWS}
|
||
procedure TBitmap.LoadFromResourceID(Instance: THandle; ResID: Integer);
|
||
var
|
||
Stream: TCustomMemoryStream;
|
||
begin
|
||
Stream := TResourceStream.CreateFromID(Instance, ResID, RT_BITMAP);
|
||
try
|
||
ReadDIB(Stream, Stream.Size);
|
||
finally
|
||
Stream.Free;
|
||
end;
|
||
end;
|
||
{$ENDIF}
|
||
|
||
procedure TBitmap.NewImage(NewHandle: HBITMAP; NewPalette: HPALETTE;
|
||
const NewDIB: TDIBSection; OS2Format: Boolean; RLEStream: TStream = nil);
|
||
var
|
||
Image: TBitmapImage;
|
||
begin
|
||
Image := TBitmapImage.Create;
|
||
with Image do
|
||
try
|
||
FHandle := NewHandle;
|
||
FPalette := NewPalette;
|
||
FDIB := NewDIB;
|
||
FOS2Format := OS2Format;
|
||
if FDIB.dsbm.bmBits <> nil then FDIBHandle := FHandle;
|
||
FSaveStream := RLEStream as TMemoryStream;
|
||
except
|
||
Image.Free;
|
||
raise;
|
||
end;
|
||
//!! replace with InterlockedExchange()
|
||
EnterCriticalSection(BitmapImageLock);
|
||
try
|
||
FImage.Release;
|
||
FImage := Image;
|
||
FImage.Reference;
|
||
finally
|
||
LeaveCriticalSection(BitmapImageLock);
|
||
end;
|
||
FMaskValid := False;
|
||
end;
|
||
|
||
procedure TBitmap.ReadData(Stream: TStream);
|
||
var
|
||
Size: Longint;
|
||
begin
|
||
Stream.Read(Size, SizeOf(Size));
|
||
ReadStream(Stream, Size);
|
||
end;
|
||
|
||
procedure TBitmap.ReadDIB(Stream: TStream; ImageSize: LongWord; bmf: PBitmapFileHeader);
|
||
const
|
||
DIBPalSizes: array [Boolean] of Byte = (sizeof(TRGBQuad), sizeof(TRGBTriple));
|
||
var
|
||
DC, MemDC: HDC;
|
||
BitsMem: Pointer;
|
||
OS2Header: TBitmapCoreHeader;
|
||
BitmapInfo: PBitmapInfo;
|
||
ColorTable: Pointer;
|
||
HeaderSize: Integer;
|
||
OS2Format: Boolean;
|
||
BMHandle, OldBMP: HBITMAP;
|
||
DIB: TDIBSection;
|
||
Pal, OldPal: HPalette;
|
||
RLEStream: TStream;
|
||
vbmf: TBitmapFileHeader;
|
||
{$IFDEF LINUX}
|
||
I: Integer;
|
||
{$ENDIF}
|
||
begin
|
||
Pal := 0;
|
||
BMHandle := 0;
|
||
RLEStream := nil;
|
||
Stream.Read(HeaderSize, sizeof(HeaderSize));
|
||
OS2Format := HeaderSize = sizeof(OS2Header);
|
||
if OS2Format then HeaderSize := sizeof(TBitmapInfoHeader);
|
||
GetMem(BitmapInfo, HeaderSize + 12 + 256 * sizeof(TRGBQuad));
|
||
with BitmapInfo^ do
|
||
try
|
||
try
|
||
if OS2Format then // convert OS2 DIB to Win DIB
|
||
begin
|
||
Stream.Read(Pointer(Longint(@OS2Header) + sizeof(HeaderSize))^,
|
||
sizeof(OS2Header) - sizeof(HeaderSize));
|
||
FillChar(bmiHeader, sizeof(bmiHeader), 0);
|
||
with bmiHeader, OS2Header do
|
||
begin
|
||
biWidth := bcWidth;
|
||
biHeight := bcHeight;
|
||
biPlanes := bcPlanes;
|
||
biBitCount := bcBitCount;
|
||
end;
|
||
Dec(ImageSize, sizeof(OS2Header));
|
||
end
|
||
else
|
||
begin // support bitmap headers larger than TBitmapInfoHeader
|
||
Stream.Read(Pointer(Longint(BitmapInfo) + sizeof(HeaderSize))^,
|
||
HeaderSize - sizeof(HeaderSize));
|
||
Dec(ImageSize, HeaderSize);
|
||
|
||
if (bmiHeader.biCompression <> BI_BITFIELDS) and
|
||
(bmiHeader.biCompression <> BI_RGB) then
|
||
begin // Preserve funky non-DIB data (like RLE) until modified
|
||
RLEStream := TMemoryStream.Create;
|
||
// source stream could be unidirectional. don't reverse seek
|
||
if bmf = nil then
|
||
begin
|
||
FillChar(vbmf, sizeof(vbmf), 0);
|
||
vbmf.bfType := $4D42;
|
||
vbmf.bfSize := ImageSize + Cardinal(HeaderSize);
|
||
bmf := @vbmf;
|
||
end;
|
||
RLEStream.Write(bmf^, sizeof(bmf^));
|
||
RLEStream.Write(HeaderSize, sizeof(HeaderSize));
|
||
RLEStream.Write(Pointer(Longint(BitmapInfo) + sizeof(HeaderSize))^,
|
||
HeaderSize - sizeof(HeaderSize));
|
||
RLEStream.CopyFrom(Stream, ImageSize);
|
||
{ Cast ImageSize (long word) to integer to avoid integer overflow when negating. }
|
||
RLEStream.Seek(-Integer(ImageSize), soFromEnd);
|
||
Stream := RLEStream; // the rest of the proc reads from RLEStream
|
||
end;
|
||
end;
|
||
|
||
with bmiHeader do
|
||
begin
|
||
biSize := HeaderSize;
|
||
ColorTable := Pointer(Longint(BitmapInfo) + HeaderSize);
|
||
|
||
{ check number of planes. DIBs must be 1 color plane (packed pixels) }
|
||
if biPlanes <> 1 then InvalidBitmap;
|
||
|
||
// 3 DWORD color element bit masks (ie 888 or 565) can precede colors
|
||
// TBitmapInfoHeader sucessors include these masks in the headersize
|
||
if (HeaderSize = sizeof(TBitmapInfoHeader)) and
|
||
((biBitCount = 16) or (biBitCount = 32)) and
|
||
(biCompression = BI_BITFIELDS) then
|
||
begin
|
||
Stream.ReadBuffer(ColorTable^, 3 * sizeof(DWORD));
|
||
Inc(Longint(ColorTable), 3 * sizeof(DWORD));
|
||
Dec(ImageSize, 3 * sizeof(DWORD));
|
||
end;
|
||
|
||
// Read the color palette
|
||
if biClrUsed = 0 then
|
||
biClrUsed := GetDInColors(biBitCount);
|
||
Stream.ReadBuffer(ColorTable^, biClrUsed * DIBPalSizes[OS2Format]);
|
||
Dec(ImageSize, biClrUsed * DIBPalSizes[OS2Format]);
|
||
|
||
// biSizeImage can be zero. If zero, compute the size.
|
||
if biSizeImage = 0 then // top-down DIBs have negative height
|
||
biSizeImage := BytesPerScanLine(biWidth, biBitCount, 32) * Abs(biHeight);
|
||
|
||
if biSizeImage < ImageSize then ImageSize := biSizeImage;
|
||
end;
|
||
|
||
{ convert OS2 color table to DIB color table }
|
||
if OS2Format then RGBTripleToQuad(ColorTable^);
|
||
|
||
DC := GDICheck(GetDC(0));
|
||
try
|
||
if ((bmiHeader.biCompression <> BI_RGB) and
|
||
(bmiHeader.biCompression <> BI_BITFIELDS)) or DDBsOnly then
|
||
begin
|
||
MemDC := 0;
|
||
GetMem(BitsMem, ImageSize);
|
||
try
|
||
Stream.ReadBuffer(BitsMem^, ImageSize);
|
||
MemDC := GDICheck(CreateCompatibleDC(DC));
|
||
OldBMP := SelectObject(MemDC, CreateCompatibleBitmap(DC, 1, 1));
|
||
OldPal := 0;
|
||
if bmiHeader.biClrUsed > 0 then
|
||
begin
|
||
Pal := PaletteFromDIBColorTable(0, ColorTable, bmiHeader.biClrUsed);
|
||
OldPal := SelectPalette(MemDC, Pal, False);
|
||
RealizePalette(MemDC);
|
||
end;
|
||
|
||
try
|
||
BMHandle := CreateDIBitmap(MemDC, BitmapInfo^.bmiHeader, CBM_INIT, BitsMem,
|
||
BitmapInfo^, DIB_RGB_COLORS);
|
||
if (BMHandle = 0) then
|
||
if GetLastError = 0 then InvalidBitmap else RaiseLastOSError;
|
||
finally
|
||
if OldPal <> 0 then
|
||
SelectPalette(MemDC, OldPal, True);
|
||
DeleteObject(SelectObject(MemDC, OldBMP));
|
||
end;
|
||
finally
|
||
if MemDC <> 0 then DeleteDC(MemDC);
|
||
FreeMem(BitsMem);
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
BMHandle := CreateDIBSection(DC, BitmapInfo^, DIB_RGB_COLORS, BitsMem, 0, 0);
|
||
if (BMHandle = 0) or (BitsMem = nil) then
|
||
if GetLastError = 0 then InvalidBitmap else RaiseLastOSError;
|
||
|
||
try
|
||
{$IFDEF LINUX}
|
||
// I need to pre-touch the memory in 4096 byte increments to ensure
|
||
// the read will succeed. WINE marks this memory as not present to
|
||
// catch when we make changes to it. If we read directly into it
|
||
// Linux will (correctly) terminate the read with a failure since an
|
||
// exception occured during the read. We need to make sure these
|
||
// exceptions are triggered in user space instead of kernel.
|
||
for I := 1 to (ImageSize + 4095) div 4096 do
|
||
PByteArray(BitsMem)^[(I - 1) * 4096] := 0;
|
||
{$ENDIF}
|
||
Stream.ReadBuffer(BitsMem^, ImageSize);
|
||
except
|
||
DeleteObject(BMHandle);
|
||
raise;
|
||
end;
|
||
end;
|
||
finally
|
||
ReleaseDC(0, DC);
|
||
end;
|
||
// Hi-color DIBs don't preserve color table, so create palette now
|
||
if (bmiHeader.biBitCount > 8) and (bmiHeader.biClrUsed > 0) and (Pal = 0)then
|
||
Pal := PaletteFromDIBColorTable(0, ColorTable, bmiHeader.biClrUsed);
|
||
|
||
FillChar(DIB, sizeof(DIB), 0);
|
||
GetObject(BMHandle, Sizeof(DIB), @DIB);
|
||
// GetObject / CreateDIBSection don't preserve these info values
|
||
DIB.dsBmih.biXPelsPerMeter := bmiHeader.biXPelsPerMeter;
|
||
DIB.dsBmih.biYPelsPerMeter := bmiHeader.biYPelsPerMeter;
|
||
DIB.dsBmih.biClrUsed := bmiHeader.biClrUsed;
|
||
DIB.dsBmih.biClrImportant := bmiHeader.biClrImportant;
|
||
except
|
||
RLEStream.Free;
|
||
raise;
|
||
end;
|
||
finally
|
||
FreeMem(BitmapInfo);
|
||
end;
|
||
NewImage(BMHandle, Pal, DIB, OS2Format, RLEStream);
|
||
PaletteModified := Palette <> 0;
|
||
Changed(Self);
|
||
end;
|
||
|
||
procedure TBitmap.ReadStream(Stream: TStream; Size: Longint);
|
||
var
|
||
Bmf: TBitmapFileHeader;
|
||
DIB: TDIBSection;
|
||
begin
|
||
FreeContext;
|
||
if Size = 0 then
|
||
begin
|
||
FillChar(DIB, sizeof(DIB), 0);
|
||
NewImage(0, 0, DIB, False);
|
||
end
|
||
else
|
||
begin
|
||
Stream.ReadBuffer(Bmf, sizeof(Bmf));
|
||
if Bmf.bfType <> $4D42 then InvalidBitmap;
|
||
ReadDIB(Stream, Size - sizeof(Bmf), @Bmf);
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetHandle(Value: HBITMAP);
|
||
var
|
||
DIB: TDIBSection;
|
||
APalette: HPALETTE;
|
||
begin
|
||
with FImage do
|
||
if FHandle <> Value then
|
||
begin
|
||
FreeContext;
|
||
FillChar(DIB, sizeof(DIB), 0);
|
||
if Value <> 0 then
|
||
GetObject(Value, SizeOf(DIB), @DIB);
|
||
if FRefCount = 1 then
|
||
begin
|
||
APalette := FPalette;
|
||
FPalette := 0;
|
||
end
|
||
else
|
||
if FPalette = SystemPalette16 then
|
||
APalette := SystemPalette16
|
||
else
|
||
APalette := CopyPalette(FPalette);
|
||
try
|
||
NewImage(Value, APalette, DIB, False);
|
||
except
|
||
InternalDeletePalette(APalette);
|
||
raise;
|
||
end;
|
||
Changed(Self);
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetHandleType(Value: TBitmapHandleType);
|
||
var
|
||
DIB: TDIBSection;
|
||
AHandle: HBITMAP;
|
||
NewPalette: HPALETTE;
|
||
DoCopy: Boolean;
|
||
begin
|
||
if Value = GetHandleType then Exit;
|
||
with FImage do
|
||
begin
|
||
if (FHandle = 0) and (FDIBHandle = 0) then
|
||
if Value = bmDDB then
|
||
FDIB.dsbmih.biSize := 0
|
||
else
|
||
FDIB.dsbmih.biSize := sizeof(FDIB.dsbmih)
|
||
else
|
||
begin
|
||
if Value = bmDIB then
|
||
begin
|
||
if (FDIBHandle <> 0) and (FDIBHandle = FHandle) then Exit;
|
||
FreeContext;
|
||
PaletteNeeded;
|
||
DIBNeeded;
|
||
if FRefCount = 1 then
|
||
begin
|
||
AHandle := FDIBHandle;
|
||
FDIBHandle := 0;
|
||
NewPalette := FPalette;
|
||
FPalette := 0;
|
||
NewImage(AHandle, NewPalette, FDIB, FOS2Format);
|
||
end
|
||
else
|
||
CopyImage(FDIBHandle, FPalette, FDIB);
|
||
end
|
||
else
|
||
begin
|
||
if (FHandle <> 0) and (FHandle <> FDIBHandle) then Exit;
|
||
FreeContext;
|
||
PaletteNeeded;
|
||
DIB := FDIB;
|
||
DIB.dsbmih.biSize := 0; // flag to tell CopyBitmap to create a DDB
|
||
DoCopy := FRefCount = 1;
|
||
if DoCopy then
|
||
NewPalette := FPalette
|
||
else
|
||
NewPalette := CopyPalette(FPalette);
|
||
AHandle := CopyBitmap(FDIBHandle, FPalette, NewPalette, DIB, nil);
|
||
if DoCopy then
|
||
FHandle := AHandle
|
||
else
|
||
NewImage(AHandle, NewPalette, DIB, FOS2Format);
|
||
end;
|
||
Changed(Self);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetHeight(Value: Integer);
|
||
var
|
||
DIB: TDIBSection;
|
||
begin
|
||
with FImage do
|
||
if FDIB.dsbm.bmHeight <> Value then
|
||
begin
|
||
HandleNeeded;
|
||
DIB := FDIB;
|
||
DIB.dsbm.bmHeight := Value;
|
||
DIB.dsbmih.biHeight := Value;
|
||
CopyImage(FHandle, FPalette, DIB);
|
||
Changed(Self);
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetMaskHandle(Value: HBITMAP);
|
||
begin
|
||
with FImage do
|
||
if FMaskHandle <> Value then
|
||
begin
|
||
FMaskHandle := Value;
|
||
FMaskValid := True;
|
||
FMaskBitsValid := True;
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetMonochrome(Value: Boolean);
|
||
var
|
||
DIB: TDIBSection;
|
||
begin
|
||
with FImage, FDIB.dsbmih do
|
||
if Value <> ((biPlanes = 1) and (biBitCount = 1)) then
|
||
begin
|
||
HandleNeeded;
|
||
DIB := FDIB;
|
||
with DIB.dsbmih, DIB.dsbm do
|
||
begin
|
||
biSize := 0; // request DDB handle
|
||
biPlanes := Byte(Value); // 0 = request screen BMP format
|
||
biBitCount := Byte(Value);
|
||
bmPlanes := Byte(Value);
|
||
bmBitsPixel := Byte(Value);
|
||
end;
|
||
CopyImage(FHandle, FPalette, DIB);
|
||
Changed(Self);
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetPalette(Value: HPALETTE);
|
||
var
|
||
AHandle: HBITMAP;
|
||
DIB: TDIBSection;
|
||
begin
|
||
if FImage.FPalette <> Value then
|
||
begin
|
||
with FImage do
|
||
if (Value = 0) and (FRefCount = 1) then
|
||
begin
|
||
InternalDeletePalette(FPalette);
|
||
FPalette := 0;
|
||
end
|
||
else
|
||
begin
|
||
FreeContext;
|
||
HandleNeeded;
|
||
DIB := FDIB;
|
||
AHandle := CopyBitmap(FHandle, FPalette, Value, DIB, nil);
|
||
try
|
||
NewImage(AHandle, Value, DIB, FOS2Format);
|
||
except
|
||
DeleteObject(AHandle);
|
||
raise;
|
||
end;
|
||
end;
|
||
UpdateDIBColorTable(FImage.FDIBHandle, Value, FImage.FDIB);
|
||
PaletteModified := True;
|
||
Changed(Self);
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetPixelFormat(Value: TPixelFormat);
|
||
const
|
||
BitCounts: array [pf1Bit..pf32Bit] of Byte = (1,4,8,16,16,24,32);
|
||
var
|
||
DIB: TDIBSection;
|
||
Pal: HPalette;
|
||
DC: HDC;
|
||
KillPal: Boolean;
|
||
begin
|
||
if Value = GetPixelFormat then Exit;
|
||
case Value of
|
||
pfDevice:
|
||
begin
|
||
HandleType := bmDDB;
|
||
Exit;
|
||
end;
|
||
pfCustom: InvalidGraphic(@SInvalidPixelFormat);
|
||
else
|
||
FillChar(DIB, sizeof(DIB), 0);
|
||
DIB.dsbm := FImage.FDIB.dsbm;
|
||
KillPal := False;
|
||
with DIB, dsbm, dsbmih do
|
||
begin
|
||
bmBits := nil;
|
||
biSize := sizeof(DIB.dsbmih);
|
||
biWidth := bmWidth;
|
||
biHeight := bmHeight;
|
||
biPlanes := 1;
|
||
biBitCount := BitCounts[Value];
|
||
Pal := FImage.FPalette;
|
||
case Value of
|
||
pf4Bit: Pal := SystemPalette16;
|
||
pf8Bit:
|
||
begin
|
||
DC := GDICheck(GetDC(0));
|
||
Pal := CreateHalftonePalette(DC);
|
||
KillPal := True;
|
||
ReleaseDC(0, DC);
|
||
end;
|
||
pf16Bit:
|
||
begin
|
||
biCompression := BI_BITFIELDS;
|
||
dsBitFields[0] := $F800;
|
||
dsBitFields[1] := $07E0;
|
||
dsBitFields[2] := $001F;
|
||
end;
|
||
end;
|
||
try
|
||
CopyImage(Handle, Pal, DIB);
|
||
PaletteModified := Pal <> 0;
|
||
finally
|
||
if KillPal then DeleteObject(Pal);
|
||
end;
|
||
Changed(Self);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetTransparentColor(Value: TColor);
|
||
begin
|
||
if Value <> FTransparentColor then
|
||
begin
|
||
if Value = clDefault then
|
||
FTransparentMode := tmAuto else
|
||
FTransparentMode := tmFixed;
|
||
FTransparentColor := Value;
|
||
if FImage.FRefCount > 1 then
|
||
with FImage do
|
||
begin
|
||
HandleNeeded;
|
||
CopyImage(FHandle, FPalette, FDIB);
|
||
end;
|
||
Changed(Self);
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetTransparentMode(Value: TTransparentMode);
|
||
begin
|
||
if Value <> FTransparentMode then
|
||
begin
|
||
if Value = tmAuto then
|
||
SetTransparentColor(clDefault) else
|
||
SetTransparentColor(GetTransparentColor);
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.SetWidth(Value: Integer);
|
||
var
|
||
DIB: TDIBSection;
|
||
begin
|
||
with FImage do
|
||
if FDIB.dsbm.bmWidth <> Value then
|
||
begin
|
||
HandleNeeded;
|
||
DIB := FDIB;
|
||
DIB.dsbm.bmWidth := Value;
|
||
DIB.dsbmih.biWidth := Value;
|
||
CopyImage(FHandle, FPalette, DIB);
|
||
Changed(Self);
|
||
end;
|
||
end;
|
||
|
||
procedure TBitmap.WriteData(Stream: TStream);
|
||
begin
|
||
WriteStream(Stream, True);
|
||
end;
|
||
|
||
procedure TBitmap.WriteStream(Stream: TStream; WriteSize: Boolean);
|
||
const
|
||
PalSize: array [Boolean] of Byte = (sizeof(TRGBQuad), sizeof(TRGBTriple));
|
||
var
|
||
Size, ColorCount: DWORD;
|
||
HeaderSize: DWORD;
|
||
BMF: TBitmapFileHeader;
|
||
Save: THandle;
|
||
BC: TBitmapCoreHeader;
|
||
Colors: array [Byte] of TRGBQuad;
|
||
begin
|
||
FillChar(BMF, sizeof(BMF), 0);
|
||
BMF.bfType := $4D42;
|
||
if FImage.FSaveStream <> nil then
|
||
begin
|
||
Size := FImage.FSaveStream.Size;
|
||
if WriteSize then
|
||
Stream.WriteBuffer(Size, sizeof(Size));
|
||
Stream.Write(FImage.FSaveStream.Memory^, FImage.FSaveStream.Size);
|
||
Exit;
|
||
end;
|
||
DIBNeeded;
|
||
with FImage do
|
||
begin
|
||
Size := 0;
|
||
if FDIBHandle <> 0 then
|
||
begin
|
||
InternalGetDIBSizes(FDIBHandle, HeaderSize, Size, FDIB.dsbmih.biClrUsed);
|
||
if FOS2Format then
|
||
begin // OS2 format cannot have partial palette
|
||
HeaderSize := sizeof(BC);
|
||
if FDIB.dsbmih.biBitCount <= 8 then
|
||
Inc(HeaderSize, sizeof(TRGBTriple) * (1 shl FDIB.dsbmih.biBitCount));
|
||
end;
|
||
Inc(Size, HeaderSize + sizeof(BMF));
|
||
|
||
FillChar(BMF, sizeof(BMF), 0);
|
||
BMF.bfType := $4D42;
|
||
|
||
Canvas.RequiredState([csHandleValid]);
|
||
Save := GDICheck(SelectObject(FCanvas.FHandle, FDIBHandle));
|
||
ColorCount := GetDIBColorTable(FCanvas.FHandle, 0, 256, Colors);
|
||
SelectObject(FCanvas.FHandle, Save);
|
||
// GetDIBColorTable always reports the full palette; trim it back for partial palettes
|
||
if (0 < FDIB.dsbmih.biClrUsed) and (FDIB.dsbmih.biClrUsed < ColorCount) then
|
||
ColorCount := FDIB.dsbmih.biClrUsed;
|
||
if (not FOS2Format) and (ColorCount = 0) and (FPalette <> 0) and not FHalftone then
|
||
begin
|
||
ColorCount := PaletteToDIBColorTable(FPalette, Colors);
|
||
if FDIB.dsbmih.biBitCount > 8 then
|
||
begin // optional color palette for hicolor images (non OS2)
|
||
Inc(Size, ColorCount * sizeof(TRGBQuad));
|
||
Inc(HeaderSize, ColorCount * sizeof(TRGBQuad));
|
||
end;
|
||
end;
|
||
|
||
BMF.bfSize := Size;
|
||
BMF.bfOffBits := sizeof(BMF) + HeaderSize;
|
||
end;
|
||
|
||
if WriteSize then Stream.WriteBuffer(Size, SizeOf(Size));
|
||
|
||
if Size <> 0 then
|
||
begin
|
||
FixupBitFields(FDIB);
|
||
if (ColorCount <> 0) then
|
||
begin
|
||
if (FDIB.dsbmih.biClrUsed = 0) or (FDIB.dsbmih.biClrUsed <> ColorCount) then
|
||
FDIB.dsbmih.biClrUsed := ColorCount;
|
||
if FOS2Format then RGBQuadToTriple(Colors, Integer(ColorCount));
|
||
end;
|
||
if FOS2Format then
|
||
begin
|
||
with BC, FDIB.dsbmih do
|
||
begin
|
||
bcSize := sizeof(BC);
|
||
bcWidth := biWidth;
|
||
bcHeight := biHeight;
|
||
bcPlanes := 1;
|
||
bcBitCount := biBitCount;
|
||
end;
|
||
Stream.WriteBuffer(BMF, sizeof(BMF));
|
||
Stream.WriteBuffer(BC, sizeof(BC));
|
||
end
|
||
else
|
||
begin
|
||
Stream.WriteBuffer(BMF, Sizeof(BMF));
|
||
Stream.WriteBuffer(FDIB.dsbmih, Sizeof(FDIB.dsbmih));
|
||
if (FDIB.dsbmih.biBitCount > 8) and
|
||
((FDIB.dsbmih.biCompression and BI_BITFIELDS) <> 0) then
|
||
Stream.WriteBuffer(FDIB.dsBitfields, 12);
|
||
end;
|
||
Stream.WriteBuffer(Colors, ColorCount * PalSize[FOS2Format]);
|
||
Stream.WriteBuffer(FDIB.dsbm.bmBits^, FDIB.dsbmih.biSizeImage);
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
{ ReleaseHandle gives up ownership of the bitmap handle the TBitmap contains. }
|
||
function TBitmap.ReleaseHandle: HBITMAP;
|
||
begin
|
||
HandleNeeded;
|
||
Changing(Self);
|
||
with FImage do
|
||
begin
|
||
Result := FHandle;
|
||
if FHandle = FDIBHandle then
|
||
begin
|
||
FDIBHandle := 0;
|
||
FDIB.dsbm.bmBits := nil;
|
||
end;
|
||
FHandle := 0;
|
||
end;
|
||
end;
|
||
|
||
function TBitmap.ReleaseMaskHandle: HBITMAP;
|
||
begin
|
||
Result := GetMaskHandle;
|
||
FImage.FMaskHandle := 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);
|
||
var
|
||
DIB: TDIBSection;
|
||
begin
|
||
Format := CF_BITMAP;
|
||
HandleNeeded;
|
||
with FImage do
|
||
begin
|
||
DIB := FDIB;
|
||
DIB.dsbmih.biSize := 0; // copy to device bitmap
|
||
DIB.dsbm.bmBits := nil;
|
||
Data := CopyBitmap(FHandle, FPalette, FPalette, DIB, FCanvas);
|
||
end;
|
||
try
|
||
APalette := CopyPalette(FImage.FPalette);
|
||
except
|
||
DeleteObject(Data);
|
||
raise;
|
||
end;
|
||
end;
|
||
|
||
function TBitmap.TransparentColorStored: Boolean;
|
||
begin
|
||
Result := FTransparentMode = tmFixed;
|
||
end;
|
||
|
||
{ TIconImage }
|
||
|
||
destructor TIconImage.Destroy;
|
||
begin
|
||
FMemoryImage.Free;
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TIconImage.FreeHandle;
|
||
begin
|
||
if FHandle <> 0 then DestroyIcon(FHandle);
|
||
FHandle := 0;
|
||
end;
|
||
|
||
{ TIcon }
|
||
|
||
constructor TIcon.Create;
|
||
begin
|
||
inherited Create;
|
||
FTransparent := True;
|
||
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]);
|
||
DrawIconEx(ACanvas.FHandle, X, Y, Handle, 0, 0, 0, 0, DI_NORMAL);
|
||
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.HandleAllocated: Boolean;
|
||
begin
|
||
Result := Assigned(FImage) and (FImage.FHandle <> 0);
|
||
end;
|
||
|
||
function TIcon.GetHeight: Integer;
|
||
begin
|
||
Result := FImage.FSize.y;
|
||
if Result = 0 then
|
||
Result := GetSystemMetrics(SM_CYICON)
|
||
end;
|
||
|
||
function TIcon.GetWidth: Integer;
|
||
begin
|
||
Result := FImage.FSize.X;
|
||
if Result = 0 then
|
||
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),
|
||
FRequestedSize, FSize);
|
||
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
|
||
if FImage.FHandle = 0 then
|
||
FRequestedSize.Y := Value
|
||
else
|
||
InvalidOperation(@SChangeIconSize);
|
||
end;
|
||
|
||
procedure TIcon.SetTransparent(Value: Boolean);
|
||
begin
|
||
// Ignore assignments to this property.
|
||
// Icons are always transparent.
|
||
end;
|
||
|
||
procedure TIcon.SetWidth(Value: Integer);
|
||
begin
|
||
if FImage.FHandle = 0 then
|
||
FRequestedSize.X := Value
|
||
else
|
||
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
|
||
Filters: string;
|
||
begin
|
||
GetFileFormats.BuildFilterStrings(GraphicClass, Result, Filters);
|
||
end;
|
||
|
||
function GraphicExtension(GraphicClass: TGraphicClass): string;
|
||
var
|
||
I: Integer;
|
||
begin
|
||
for I := GetFileFormats.Count-1 downto 0 do
|
||
if PFileFormat(FileFormats[I])^.GraphicClass.ClassName = GraphicClass.ClassName then
|
||
begin
|
||
Result := PFileFormat(FileFormats[I])^.Extension;
|
||
Exit;
|
||
end;
|
||
Result := '';
|
||
end;
|
||
|
||
function GraphicFileMask(GraphicClass: TGraphicClass): string;
|
||
var
|
||
Descriptions: string;
|
||
begin
|
||
GetFileFormats.BuildFilterStrings(GraphicClass, Descriptions, Result);
|
||
end;
|
||
|
||
procedure InitScreenLogPixels;
|
||
const
|
||
Pal16: array [0..15] of TColor =
|
||
(clBlack, clMaroon, clGreen, clOlive, clNavy, clPurple, clTeal, clDkGray,
|
||
clLtGray, clRed, clLime, clYellow, clBlue, clFuchsia, clAqua, clWhite);
|
||
var
|
||
DC: HDC;
|
||
begin
|
||
DC := GetDC(0);
|
||
ScreenLogPixels := GetDeviceCaps(DC, LOGPIXELSY);
|
||
ReleaseDC(0,DC);
|
||
//!! SystemPalette16 := GetStockObject(DEFAULT_PALETTE);
|
||
SystemPalette16 := CreateSystemPalette(Pal16);
|
||
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 := '<27>l<EFBFBD>r <20>o<EFBFBD>S<EFBFBD>V<EFBFBD>b<EFBFBD>N';
|
||
DefFontData.Height := -MulDiv(9, ScreenLogPixels, 72);
|
||
DefFontData.CharSet := CharSet;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
type
|
||
PPattern = ^TPattern;
|
||
TPattern = record
|
||
Next: PPattern;
|
||
Bitmap: TBitmap;
|
||
BkColorRef: TColorRef;
|
||
FgColorRef: TColorRef;
|
||
end;
|
||
|
||
TPatternManager = class(TMyObject)
|
||
private
|
||
List: PPattern;
|
||
FLock: TRTLCriticalSection;
|
||
function CreateBitmap(BkColor, FgColor: TColor): TBitmap;
|
||
public
|
||
constructor Create;
|
||
destructor Destroy; override;
|
||
function AllocPattern(BkColor, FgColor: TColorRef): PPattern;
|
||
procedure FreePatterns;
|
||
procedure Lock;
|
||
procedure Unlock;
|
||
end;
|
||
|
||
constructor TPatternManager.Create;
|
||
begin
|
||
InitializeCriticalSection(FLock);
|
||
end;
|
||
|
||
destructor TPatternManager.Destroy;
|
||
begin
|
||
FreePatterns;
|
||
DeleteCriticalSection(FLock);
|
||
end;
|
||
|
||
procedure TPatternManager.Lock;
|
||
begin
|
||
EnterCriticalSection(FLock);
|
||
end;
|
||
|
||
procedure TPatternManager.Unlock;
|
||
begin
|
||
LeaveCriticalSection(FLock);
|
||
end;
|
||
|
||
function TPatternManager.AllocPattern(BkColor, FgColor: TColorRef): PPattern;
|
||
begin
|
||
Lock;
|
||
try
|
||
Result := List;
|
||
while (Result <> nil) and ((Result^.BkColorRef <> BkColor) or
|
||
(Result^.FgColorRef <> FgColor)) do
|
||
Result := Result^.Next;
|
||
if Result = nil then
|
||
begin
|
||
GetMem(Result, SizeOf(TPattern));
|
||
with Result^ do
|
||
begin
|
||
Next := List;
|
||
Bitmap := CreateBitmap(BkColor, FgColor);
|
||
BkColorRef := BkColor;
|
||
FgColorRef := FgColor;
|
||
end;
|
||
List := Result;
|
||
end;
|
||
finally
|
||
Unlock;
|
||
end;
|
||
end;
|
||
|
||
function TPatternManager.CreateBitmap(BkColor, FgColor: TColor): TBitmap;
|
||
var
|
||
X, Y: Integer;
|
||
begin
|
||
Result := TBitmap.Create;
|
||
try
|
||
with Result do
|
||
begin
|
||
Width := 8;
|
||
Height := 8;
|
||
with Canvas do
|
||
begin
|
||
Brush.Style := bsSolid;
|
||
Brush.Color := BkColor;
|
||
FillRect(Rect(0, 0, Width, Height));
|
||
for Y := 0 to 8 do
|
||
for X := 0 to 8 do
|
||
if (Y mod 2) = (X mod 2) then { toggles between even/odd pixles }
|
||
Pixels[X, Y] := FgColor; { on even/odd rows }
|
||
end;
|
||
Dormant;
|
||
end;
|
||
except
|
||
Result.Free;
|
||
raise;
|
||
end;
|
||
end;
|
||
|
||
procedure TPatternManager.FreePatterns;
|
||
var
|
||
P: PPattern;
|
||
begin
|
||
while List <> nil do
|
||
begin
|
||
P := List;
|
||
with P^ do
|
||
begin
|
||
Lock;
|
||
try
|
||
List := Next
|
||
finally
|
||
Unlock;
|
||
end;
|
||
if Bitmap <> nil then Bitmap.Free;
|
||
end;
|
||
FreeMem(P);
|
||
end;
|
||
end;
|
||
|
||
var
|
||
PatternManager: TPatternManager;
|
||
|
||
|
||
function AllocPatternBitmap(BkColor, FgColor: TColor): TBitmap;
|
||
begin
|
||
if PatternManager <> nil then
|
||
Result := PatternManager.AllocPattern(ColorToRGB(BkColor),
|
||
ColorToRGB(FgColor)).Bitmap
|
||
else
|
||
Result := nil;
|
||
end;
|
||
|
||
|
||
|
||
initialization
|
||
InitScreenLogPixels;
|
||
InitializeCriticalSection(BitmapImageLock);
|
||
InitializeCriticalSection(CounterLock);
|
||
StockPen := GetStockObject(BLACK_PEN);
|
||
StockBrush := GetStockObject(HOLLOW_BRUSH);
|
||
StockFont := GetStockObject(SYSTEM_FONT);
|
||
StockIcon := LoadIcon(0, IDI_APPLICATION);
|
||
InitDefFontData;
|
||
FontManager := TResourceManager.Create(SizeOf(TFontData));
|
||
PenManager := TResourceManager.Create(SizeOf(TPenData));
|
||
BrushManager := TResourceManager.Create(SizeOf(TBrushData));
|
||
PatternManager := TPatternManager.Create;
|
||
BitmapCanvasList := TThreadList.Create;
|
||
CanvasList := TThreadList.Create;
|
||
RegisterIntegerConsts(TypeInfo(TColor), IdentToColor, ColorToIdent);
|
||
RegisterIntegerConsts(TypeInfo(TFontCharset), IdentToCharset, CharsetToIdent);
|
||
finalization
|
||
PatternManager.Free;
|
||
FileFormats.Free;
|
||
ClipboardFormats.Free;
|
||
FreeMemoryContexts;
|
||
BitmapCanvasList.Free;
|
||
CanvasList.Free;
|
||
FontManager.Free;
|
||
PenManager.Free;
|
||
BrushManager.Free;
|
||
DeleteObject(SystemPalette16);
|
||
DeleteCriticalSection(BitmapImageLock);
|
||
DeleteCriticalSection(CounterLock);
|
||
end.
|