git-svn-id: https://192.168.0.254/svn/Componentes.Terceros.jvcl@17 7f62d464-2af8-f54e-996c-e91b33f51cbe
2131 lines
75 KiB
ObjectPascal
2131 lines
75 KiB
ObjectPascal
(*======================================================================*
|
||
| unitExIcon.pas |
|
||
| |
|
||
| Encapsulates Windows Icons & Cursors. |
|
||
| |
|
||
| For icons In TExIconImage, the memory is the bitmapinfo, followed by |
|
||
| the color and mask bits. |
|
||
| |
|
||
| For cursors it is preceeded by word x and y hotspots |
|
||
| |
|
||
| This corresponds with what you find in resources, but not .CUR files |
|
||
| |
|
||
| .CUR files look like .ICO files except: |
|
||
| |
|
||
| 1. The wType is '2' not '1' |
|
||
| |
|
||
| 2. wPlanes and wBitCount contain the X and Y hotspot. Because of |
|
||
| they can only get the color depth from bColorCount, with its |
|
||
| max of 256. |
|
||
| |
|
||
| The contents of this file are subject to the Mozilla Public License |
|
||
| Version 1.1 (the "License"); you may not use this file except in |
|
||
| compliance with the License. You may obtain a copy of the License |
|
||
| at http://www.mozilla.org/MPL/ |
|
||
| |
|
||
| Software distributed under the License is distributed on an "AS IS" |
|
||
| basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See |
|
||
| the License for the specific language governing rights and |
|
||
| limitations under the License. |
|
||
| |
|
||
| Copyright <20> Colin Wilson 2002 All Rights Reserved |
|
||
| |
|
||
| Version Date By Description |
|
||
| ------- ---------- ---- ----------------------------------------- |
|
||
| 1.0 10/10/2000 CPWW Original |
|
||
| 1.01 30/04/2001 CPWW Cursors working |
|
||
| 1.02 17/12/2001 CPWW Bug in displaying icons/cursor in W98 |
|
||
| fixed. |
|
||
*----------------------------------------------------------------------*)
|
||
|
||
unit unitExIcon;
|
||
|
||
interface
|
||
|
||
uses Windows, Classes, SysUtils, Graphics;
|
||
|
||
type
|
||
|
||
//=============================================================================
|
||
// TExIconImage class - Shared image structure for icons & cursors
|
||
// nb. the memory image (and of course, the handle) are for one image only
|
||
|
||
|
||
// TIconHeader is variously called NEWHEADER, ICONDIR and GRPICONDIR in the SDK
|
||
TIconHeader = packed record
|
||
wReserved : word; // Must be 0
|
||
wType : word; // 1 for icons, 2 for cursors
|
||
wCount : word; // Number of components
|
||
end;
|
||
PIconHeader = ^TIconHeader;
|
||
|
||
// TResourceDirectory is called RESDIR in the SDK.
|
||
TResourceDirectory = packed record
|
||
details : packed record case boolean of
|
||
False : (cursorWidth, cursorHeight : word);
|
||
True : (iconWidth, iconHeight, iconColorCount, iconReserved : BYTE)
|
||
end;
|
||
wPlanes, wBitCount : word;
|
||
lBytesInRes : DWORD;
|
||
wNameOrdinal : word
|
||
end;
|
||
PResourceDirectory = ^TResourceDirectory;
|
||
|
||
// TIconDirEntry is called ICONDIRENTRY in the SDK
|
||
TIconDirEntry = packed record
|
||
bWidth : BYTE; // Width, in pixels, of the image
|
||
bHeight : BYTE; // Height, in pixels, of the image
|
||
bColorCount : BYTE; // Number of colors in image (0 if >=8bpp)
|
||
bReserved : BYTE; // Reserved ( must be 0)
|
||
wPlanes : WORD; // Color Planes (X Hotspot for cursors)
|
||
wBitCount : WORD; // Bits per pixel (Y Hotspot for cursors - implies MAX 256 color cursors (!))
|
||
dwBytesInRes : DWORD; // How many bytes in this resource?
|
||
dwImageOffset : DWORD; // Where in the file is this image?
|
||
end;
|
||
PIconDirEntry = ^TIconDirEntry;
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// TExIconImage
|
||
//
|
||
// Each ExIconCursor can have multiple TExIconImage classes - one per format in
|
||
// the ICO file or Icon resource/
|
||
|
||
TExIconImage = class (TSharedImage)
|
||
FIsIcon : boolean;
|
||
FHandle: HICON;
|
||
FPalette : HPALETTE;
|
||
FMemoryImage: TCustomMemoryStream;
|
||
FGotPalette : boolean; // Indicates that we've got a the palette from the image data
|
||
// or that there is no palette (eg. it's not pf1bit ..pf8Bit)
|
||
|
||
FWidth, FHeight : Integer;
|
||
FPixelFormat : TPixelFormat;
|
||
|
||
procedure HandleNeeded;
|
||
procedure PaletteNeeded;
|
||
procedure ImageNeeded;
|
||
|
||
function GetBitmapInfo : PBitmapInfo;
|
||
function GetBitmapInfoHeader : PBitmapInfoHeader;
|
||
private
|
||
function GetMemoryImage: TCustomMemoryStream;
|
||
|
||
protected
|
||
procedure FreeHandle; override;
|
||
public
|
||
destructor Destroy; override;
|
||
|
||
property Handle : HICON read fHandle; // The Icon image handle
|
||
property PaletteHandle : HPALETTE read fPalette; // The Icon image's palette
|
||
|
||
property Width : Integer read FWidth;
|
||
property Height : Integer read FHeight;
|
||
property PixelFormat : TPixelFormat read FPixelFormat;
|
||
property MemoryImage : TCustomMemoryStream read GetMemoryImage;
|
||
end;
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// TExIconCursor
|
||
|
||
TExIconCursor = class (TGraphic)
|
||
private
|
||
FImages : array of TExIconImage;
|
||
FCurrentImage : Integer;
|
||
FTransparentColor: TColor;
|
||
|
||
function GetHandle: HICON;
|
||
function GetPixelFormat: TPixelFormat;
|
||
procedure SetPixelFormat(const Value: TPixelFormat);
|
||
function GetImageCount: Integer;
|
||
|
||
procedure ReleaseImages;
|
||
function GetImage(index: Integer): TExIconImage;
|
||
procedure SetHandle(const Value: HICON);
|
||
procedure AssignFromGraphic (source : TGraphic);
|
||
procedure SetCurrentImage(const Value: Integer);
|
||
|
||
procedure HandleNeeded;
|
||
procedure PaletteNeeded;
|
||
procedure ImageNeeded;
|
||
procedure ReadIcon (instance : THandle; stream : TCustomMemoryStream; Size : Integer);
|
||
protected
|
||
procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
|
||
function GetEmpty: Boolean; override;
|
||
function GetHeight: Integer; override;
|
||
function GetWidth: Integer; override;
|
||
procedure SetHeight(Value: Integer); override;
|
||
procedure SetWidth(Value: Integer); override;
|
||
procedure SetPalette(Value: HPALETTE); override;
|
||
function GetTransparent : boolean; override;
|
||
function GetPalette : HPALETTE; override;
|
||
|
||
public
|
||
constructor Create; override;
|
||
destructor Destroy; override;
|
||
procedure LoadFromStream(Stream: TStream); override;
|
||
procedure SaveToStream(Stream: TStream); override;
|
||
procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle; APalette: HPALETTE); override;
|
||
procedure LoadFromResourceName (Instance : THandle; const resName : string);
|
||
procedure LoadFromResourceId (Instance : THandle; ResID : Integer);
|
||
procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle; var APalette: HPALETTE); override;
|
||
procedure Assign (source : TPersistent); override;
|
||
procedure AssignTo (dest : TPersistent); override;
|
||
function Releasehandle : HICON;
|
||
|
||
procedure SaveImageToFile (const FileName : string);
|
||
|
||
// Save just the current image - SaveToFile saves all the images.
|
||
|
||
|
||
property Handle: HICON read GetHandle write SetHandle;
|
||
property PixelFormat : TPixelFormat read GetPixelFormat write SetPixelFormat;
|
||
property ImageCount : Integer read GetImageCount;
|
||
property Images [index : Integer] : TExIconImage read GetImage;
|
||
|
||
property CurrentImage : Integer read fCurrentImage write SetCurrentImage;
|
||
property TransparentColor : TColor read fTransparentColor write fTransparentColor;
|
||
end;
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// TExIcon
|
||
|
||
TExIcon = class (TExIconCursor)
|
||
protected
|
||
public
|
||
constructor Create; override;
|
||
end;
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// TExCursor
|
||
|
||
TExCursor = class (TExIconCursor)
|
||
private
|
||
function GetHotspot: DWORD;
|
||
procedure SetHotspot(const Value: DWORD);
|
||
protected
|
||
public
|
||
constructor Create; override;
|
||
property Hotspot : DWORD read GetHotspot write SetHotspot;
|
||
|
||
// nb. .CUR file format is not the same as resource stream format !!!!
|
||
|
||
procedure LoadFromFile (const FileName : string); override;
|
||
procedure SaveToFile (const FileName : string); override;
|
||
end;
|
||
|
||
function GetPixelFormatNumColors (pf : TPixelFormat) : Integer;
|
||
function GetPixelFormatBitCount (pf : TPixelFormat) : Integer;
|
||
|
||
function CreateMappedBitmap (source : TGraphic; palette : HPALETTE; hiPixelFormat : TPixelFormat; Width, Height : Integer) : TBitmap;
|
||
function GetBitmapInfoNumColors (const BI : TBitmapInfoHeader) : Integer;
|
||
function GetBitmapInfoPixelFormat (const BI : TBitmapInfoHeader) : TPixelFormat;
|
||
procedure GetBitmapInfoSizes (const BI : TBitmapInfoHeader; var InfoHeaderSize, ImageSize : DWORD; iconInfo : boolean);
|
||
function GetPixelFormat (graphic : TGraphic) : TPixelFormat;
|
||
|
||
var
|
||
SystemPalette256 : HPALETTE; // 256 color 'web' palette.
|
||
SystemPalette2 : HPALETTE;
|
||
|
||
implementation
|
||
|
||
uses Clipbrd;
|
||
|
||
resourceString
|
||
rstInvalidIcon = 'Invalid Icon or Cursor';
|
||
rstInvalidCursor = 'Invalid cursor';
|
||
rstInvalidBitmap = 'Invalid Bitmap';
|
||
rstInvalidPixelFormat = 'Pixel Format Not Valid for Icons or Cursors';
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| GetPixelFormatNumColors |
|
||
| |
|
||
| Get number of colors for a pixel format. 0 if > pf8bit |
|
||
*----------------------------------------------------------------------*)
|
||
function GetPixelFormatNumColors (pf : TPixelFormat) : Integer;
|
||
begin
|
||
case pf of
|
||
pf1Bit : Result := 2;
|
||
pf4Bit : Result := 16;
|
||
pf8Bit : Result := 256;
|
||
else
|
||
Result := 0
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| GetPixelFormatBitCount |
|
||
| |
|
||
| Get number of bits per pixel for a pixel format |
|
||
*----------------------------------------------------------------------*)
|
||
function GetPixelFormatBitCount (pf : TPixelFormat) : Integer;
|
||
begin
|
||
case pf of
|
||
pf1Bit : Result := 1;
|
||
pf4Bit : Result := 4;
|
||
pf8Bit : Result := 8;
|
||
pf15Bit : Result := 16; // 16 bpp RGB. 1 unused, 5 R, 5 G, 5 B
|
||
pf16Bit : Result := 16; // 16 bpp BITFIELDS
|
||
pf24Bit : Result := 24;
|
||
pf32Bit : Result := 32 // Either RGB (8 unused, 8 R, 8 G, 8 B) or 32 bit BITFIELDS
|
||
else
|
||
Result := 0
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| GetPixelFormat |
|
||
| |
|
||
| Get our pixel format. |
|
||
*----------------------------------------------------------------------*)
|
||
function GetPixelFormat (graphic : TGraphic) : TPixelFormat;
|
||
begin
|
||
if graphic is TBitmap then
|
||
Result := TBitmap (graphic).PixelFormat
|
||
else
|
||
if graphic is TExIconCursor then
|
||
Result := TExIconCursor (graphic).PixelFormat
|
||
else
|
||
Result := pfDevice
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function GDICheck() |
|
||
| |
|
||
| Check GDI APIs |
|
||
*----------------------------------------------------------------------------*)
|
||
function GDICheck(Value: HGDIOBJ): HGDIOBJ;
|
||
begin
|
||
if Value = 0 then
|
||
RaiseLastOSError;
|
||
Result := Value;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure InitializeBitmapInfoHeader () |
|
||
| |
|
||
| Initialize a TBitmapInfoHeader from a DIB or DDB bitmap |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure InitializeBitmapInfoHeader(Bitmap: HBITMAP; var BI: TBitmapInfoHeader; PixelFormat : TPixelFormat);
|
||
var
|
||
DS: TDIBSection;
|
||
Bytes: Integer;
|
||
begin
|
||
DS.dsbmih.biSize := 0;
|
||
Bytes := GetObject(Bitmap, SizeOf(DS), @DS);
|
||
if Bytes = 0 then
|
||
raise EInvalidGraphic.Create (rstInvalidBitmap);
|
||
|
||
if (Bytes >= (sizeof(DS.dsbm) + sizeof(DS.dsbmih))) and
|
||
(DS.dsbmih.biSize >= DWORD(sizeof(DS.dsbmih))) then
|
||
BI := DS.dsbmih // It was a DIB bitmap
|
||
else
|
||
begin // It was a DDB bitmap
|
||
FillChar(BI, sizeof(BI), 0);
|
||
with BI, DS.dsbm do
|
||
begin
|
||
biSize := SizeOf(BI);
|
||
biWidth := bmWidth;
|
||
biHeight := bmHeight;
|
||
end;
|
||
end;
|
||
|
||
if PixelFormat in [pf1Bit..pf8Bit] then
|
||
begin
|
||
BI.biBitCount := GetPixelFormatBitCount (PixelFormat);
|
||
BI.biClrUsed := GetPixelFormatNumColors (PixelFormat)
|
||
end
|
||
else
|
||
begin
|
||
BI.biBitCount := DS.dsbm.bmBitsPixel * DS.dsbm.bmPlanes;
|
||
case DS.dsBm.bmBitsPixel of
|
||
1 : BI.biClrUsed := 2;
|
||
4 : BI.biClrUsed := 16;
|
||
8 : BI.biClrUsed := 256
|
||
end
|
||
end;
|
||
|
||
BI.biPlanes := 1;
|
||
if BI.biClrImportant > BI.biClrUsed then
|
||
BI.biClrImportant := BI.biClrUsed;
|
||
|
||
BI.biSizeImage := 0; // SDK sample IconPro always sets biSizeImage to 0. It
|
||
// seems to be safer to calculate the size from hight * bytes per
|
||
// scan line. So we'll do the same...
|
||
end;
|
||
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function GetBitmapInfoNumColors |
|
||
| |
|
||
| Get the number of colors (0, 2..256) of a bitmap header. |
|
||
*----------------------------------------------------------------------------*)
|
||
function GetBitmapInfoNumColors (const BI : TBitmapInfoHeader) : Integer;
|
||
begin
|
||
if BI.biBitCount <= 8 then
|
||
if BI.biClrUsed > 0 then
|
||
result := BI.biClrUsed
|
||
else
|
||
result := 1 shl BI.biBitCount
|
||
else
|
||
result := 0;
|
||
end;
|
||
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function GetBitmapInfoPixelFormat |
|
||
| |
|
||
| Get the pixel format of a bitmap header. |
|
||
*----------------------------------------------------------------------------*)
|
||
function GetBitmapInfoPixelFormat (const BI : TBitmapInfoHeader) : TPixelFormat;
|
||
begin
|
||
case BI.biBitCount of
|
||
1: result := pf1Bit;
|
||
4: result := pf4Bit;
|
||
8: result := pf8Bit;
|
||
16: case BI.biCompression of
|
||
BI_RGB : result := pf15Bit;
|
||
BI_BITFIELDS: result := pf16Bit;
|
||
else
|
||
raise EInvalidGraphic.Create (rstInvalidPixelFormat);
|
||
end;
|
||
24: result := pf24Bit;
|
||
32: result := pf32Bit;
|
||
else
|
||
raise EInvalidGraphic.Create (rstInvalidPixelFormat);
|
||
end
|
||
end;
|
||
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure GetBitmapInfoSizes |
|
||
| |
|
||
| Get the size of the info (incl the colortable), and the bitmap bits |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure GetBitmapInfoSizes (const BI : TBitmapInfoHeader; var InfoHeaderSize, ImageSize : DWORD; iconInfo : boolean);
|
||
var
|
||
numColors : Integer;
|
||
height : Integer;
|
||
begin
|
||
InfoHeaderSize := SizeOf (TBitmapInfoHeader);
|
||
|
||
numColors := GetBitmapInfoNumColors (bi);
|
||
|
||
if numColors > 0 then
|
||
Inc (InfoHeaderSize, SizeOf(TRGBQuad) * NumColors)
|
||
else
|
||
if (BI.biCompression and BI_BITFIELDS) <> 0 then
|
||
Inc(InfoHeaderSize, 12);
|
||
|
||
height := Abs(BI.biHeight);
|
||
if iconInfo then height := height shr 1;
|
||
ImageSize := BytesPerScanLine(BI.biWidth, BI.biBitCount, 32) * Height
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure InternalGetDIBSizes () |
|
||
| |
|
||
| Get size of bitmap header (incl. color table) and bitmap bits. |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure InternalGetDIBSizes(Bitmap: HBITMAP; var InfoHeaderSize: DWORD;
|
||
var ImageSize: DWORD; PixelFormat : TPixelFormat);
|
||
var
|
||
BI: TBitmapInfoHeader;
|
||
begin
|
||
InitializeBitmapInfoHeader(Bitmap, BI, PixelFormat);
|
||
GetBitmapInfoSizes (BI, InfoHeaderSize, ImageSize, False);
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure InternalGetDIB () |
|
||
| |
|
||
| Get bitmap bits. Note that we *always* call this on a bitmap with the |
|
||
| required colour depth - ie. we don't use this to do mapping. |
|
||
| |
|
||
| We (therefore) don't use GetDIBits here to get the colour table. |
|
||
*----------------------------------------------------------------------------*)
|
||
function InternalGetDIB(Bitmap: HBITMAP; Palette: HPALETTE;
|
||
BitmapInfo : PBitmapInfo; var Bits; PixelFormat : TPixelFormat): Boolean;
|
||
var
|
||
OldPal: HPALETTE;
|
||
DC: HDC;
|
||
begin
|
||
InitializeBitmapInfoHeader(Bitmap, BitmapInfo^.bmiHeader, PixelFormat);
|
||
OldPal := 0;
|
||
DC := CreateCompatibleDC(0);
|
||
try
|
||
if Palette <> 0 then
|
||
begin
|
||
OldPal := SelectPalette(DC, Palette, False);
|
||
RealizePalette(DC);
|
||
end;
|
||
Result := GetDIBits(DC, Bitmap, 0, BitmapInfo^.bmiHeader.biHeight, @Bits, BitmapInfo^, DIB_RGB_COLORS) <> 0;
|
||
finally
|
||
if OldPal <> 0 then SelectPalette(DC, OldPal, False);
|
||
DeleteDC(DC);
|
||
end;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure CreateDIBPalette () |
|
||
| |
|
||
| Create the palette from bitmap info. |
|
||
*----------------------------------------------------------------------------*)
|
||
function CreateDIBPalette (const bmi : TBitmapInfo) : HPalette;
|
||
var
|
||
lpPal : PLogPalette;
|
||
i : Integer;
|
||
numColors : Integer;
|
||
r : RGBQUAD;
|
||
begin
|
||
result := 0;
|
||
|
||
NumColors := GetBitmapInfoNumColors (bmi.bmiHeader);
|
||
|
||
if NumColors > 0 then
|
||
begin
|
||
if NumColors = 1 then
|
||
result := CopyPalette (SystemPalette2)
|
||
else
|
||
begin
|
||
GetMem (lpPal, sizeof (TLogPalette) + sizeof (TPaletteEntry) * NumColors);
|
||
try
|
||
lpPal^.palVersion := $300;
|
||
lpPal^.palNumEntries := NumColors;
|
||
|
||
{$R-}
|
||
for i := 0 to NumColors -1 do
|
||
begin
|
||
r := bmi.bmiColors [i];
|
||
lpPal^.palPalEntry[i].peRed := bmi.bmiColors [i].rgbRed;
|
||
lpPal^.palPalEntry[i].peGreen := bmi.bmiColors[i].rgbGreen;
|
||
lpPal^.palPalEntry[i].peBlue := bmi.bmiColors[i].rgbBlue;
|
||
lpPal^.palPalEntry[i].peFlags := 0 // not bmi.bmiColors[i].rgbReserved !!
|
||
end;
|
||
{$R+}
|
||
result := CreatePalette (lpPal^)
|
||
finally
|
||
FreeMem (lpPal)
|
||
end
|
||
end
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure CreateMappedBitmap |
|
||
| |
|
||
| Copy a graphic to a DIB bitmap with the specified palette or color |
|
||
| format, and size. |
|
||
| |
|
||
| If the palette is 0, the returned bitmap's pixelformat is hiPixelFormat |
|
||
| otherwise the returned bitmap's pixel format is set so it's correct for |
|
||
| the number of colors in the palette. |
|
||
*----------------------------------------------------------------------------*)
|
||
function CreateMappedBitmap (source : TGraphic; palette : HPALETTE; hiPixelFormat : TPixelFormat; Width, Height : Integer) : TBitmap;
|
||
var
|
||
colorCount : Integer;
|
||
begin
|
||
result := TBitmap.Create;
|
||
result.Width := source.Width;
|
||
result.Height := source.Height;
|
||
|
||
if palette <> 0 then
|
||
begin
|
||
colorCount := 0;
|
||
if GetObject (palette, sizeof (colorCount), @colorCount) = 0 then
|
||
RaiseLastOSError;
|
||
|
||
case colorCount of
|
||
1..2 : result.PixelFormat := pf1Bit;
|
||
3..16 : result.PixelFormat := pf4Bit;
|
||
17..256 : result.PixelFormat := pf8Bit;
|
||
else
|
||
result.PixelFormat := hiPixelFormat;
|
||
end;
|
||
|
||
result.Palette := CopyPalette (palette);
|
||
|
||
result.Canvas.StretchDraw (rect (0, 0, Width, Height), source);
|
||
end
|
||
else
|
||
begin
|
||
result.PixelFormat := hiPixelFormat;
|
||
result.Canvas.StretchDraw (rect (0, 0, Width, Height), source);
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure MaskBitmapBits |
|
||
| |
|
||
| Kinda like MaskBlt - but without the bugs. SLOW. Maybe I'll revisit this |
|
||
| use bitblt instead... |
|
||
| |
|
||
| But see MSDN PRB: Trouble Using DIBSection as a Monochrome Mask |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure MaskBitmapBits (bits : PChar; pixelFormat : TPixelFormat; mask : PChar; width, height : DWORD; palette : HPalette);
|
||
var
|
||
bpScanline, maskbpScanline : Integer;
|
||
bitsPerPixel, i, j : Integer;
|
||
maskbp, bitbp : byte;
|
||
maskp, bitp : PChar;
|
||
maskPixel : boolean;
|
||
maskByte: dword;
|
||
maskU : UINT;
|
||
maskColor : byte;
|
||
maskColorByte : byte;
|
||
begin
|
||
// Get 'black' color index. This is usually 0
|
||
// but some people play jokes...
|
||
|
||
if palette <> 0 then
|
||
begin
|
||
maskU := GetNearestPaletteIndex (palette, RGB (0, 0, 0));
|
||
if maskU = CLR_INVALID then
|
||
RaiseLastOSError;
|
||
|
||
maskColor := maskU
|
||
end
|
||
else
|
||
maskColor := 0;
|
||
|
||
bitsPerPixel := GetPixelFormatBitCount (PixelFormat);
|
||
if bitsPerPixel = 0 then
|
||
raise EInvalidGraphic.Create (rstInvalidPixelFormat);
|
||
|
||
// Get byte count for mask and bitmap
|
||
// scanline. Can be weird because of padding.
|
||
|
||
bpScanline := BytesPerScanLine(width, bitsPerPixel, 32);
|
||
maskbpScanline := BytesPerScanline (width, 1, 32);
|
||
|
||
maskByte := $ffffffff; // Set constant values for 8bpp masks
|
||
maskColorByte := maskColor;
|
||
|
||
for i := 0 to height - 1 do // Go thru each scanline...
|
||
begin
|
||
|
||
maskbp := 0; // Bit offset in current mask byte
|
||
bitbp := 0; // Bit offset in current bitmap byte
|
||
maskp := mask; // Pointer to current mask byte
|
||
bitp := bits; // Pointer to current bitmap byte;
|
||
|
||
for j := 0 to width - 1 do // Go thru each pixel
|
||
begin
|
||
// Pixel should be masked?
|
||
maskPixel := (byte (maskp^) and ($80 shr maskbp)) <> 0;
|
||
if maskPixel then
|
||
begin
|
||
case bitsPerPixel of
|
||
1, 4, 8 :
|
||
begin
|
||
case bitsPerPixel of // Calculate bit mask and 'black' color bits
|
||
1 :
|
||
begin
|
||
maskByte := $80 shr bitbp;
|
||
maskColorByte := maskColor shl (7 - bitbp);
|
||
end;
|
||
|
||
4 :
|
||
begin
|
||
maskByte := $f0 shr bitbp;
|
||
maskColorByte := maskColor shl (4 - bitbp)
|
||
end
|
||
end;
|
||
// Apply the mask
|
||
bitp^ := char ((byte (bitp^) and (not maskByte)) or maskColorByte);
|
||
end;
|
||
|
||
15, 16 :
|
||
PWORD (bitp)^ := $0000;
|
||
|
||
24 :
|
||
begin
|
||
PWORD (bitp)^ := $0000;
|
||
PBYTE (bitp + sizeof (WORD))^ := $00
|
||
end;
|
||
|
||
32 :
|
||
PDWORD (bitp)^ := $ffffffff;
|
||
end
|
||
end;
|
||
|
||
Inc (maskbp); // Next mask bit
|
||
if maskbp = 8 then
|
||
begin
|
||
maskbp := 0;
|
||
Inc (maskp) // Next mask byte
|
||
end;
|
||
|
||
Inc (bitbp, bitsPerPixel); // Next bitmap bit(s)
|
||
while bitbp >= 8 do
|
||
begin
|
||
Dec (bitbp, 8);
|
||
Inc (bitp) // Next bitmap byte
|
||
end
|
||
end;
|
||
|
||
Inc (mask, maskbpScanline); // Set mask for start of next line
|
||
Inc (bits, bpScanLine) // Set bits to start of next line
|
||
end
|
||
end;
|
||
|
||
{ TExIconCursor }
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIcon.Assign |
|
||
| |
|
||
| Assign an TExIcon from another graphic. |
|
||
| |
|
||
| A bit of a compromise this... |
|
||
| |
|
||
| ... if source is a TExIcon then all images get replaced by the source |
|
||
| images. |
|
||
| |
|
||
| ... Otherwise only the CurrentImage gets replaced |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.Assign(source: TPersistent);
|
||
var
|
||
i : Integer;
|
||
src : TExIconCursor;
|
||
image : TExIconImage;
|
||
data : THandle;
|
||
begin
|
||
if source is TExIconCursor then
|
||
begin // Share all images from the source TExIcon
|
||
src := TExIconCursor (source);
|
||
FTransparentColor := src.TransparentColor;
|
||
|
||
ReleaseImages;
|
||
SetLength (fImages, src.ImageCount);
|
||
|
||
for i := 0 to ImageCount - 1 do
|
||
begin
|
||
src.Images [i].Reference;
|
||
fImages [i] := src.Images [i]
|
||
end;
|
||
|
||
fCurrentImage := src.FCurrentImage;
|
||
Changed(Self);
|
||
end
|
||
else
|
||
if source = Nil then // Clear the current image.
|
||
begin
|
||
image := TExIconImage.Create;
|
||
image.FIsIcon := Images [FCurrentImage].FIsIcon;
|
||
image.FWidth := Images [FCurrentImage].Width;
|
||
image.FHeight := Images [FCurrentImage].Height;
|
||
image.FPixelFormat := Images [FCurrentImage].PixelFormat;
|
||
|
||
Images [fCurrentImage].Release;
|
||
FImages [FCurrentImage] := image;
|
||
image.Reference;
|
||
Changed(Self);
|
||
end
|
||
else
|
||
if source is TGraphic then // Copy from other graphic (TBitmap, etc)
|
||
AssignFromGraphic (TGraphic (source))
|
||
else
|
||
if source is TClipboard then
|
||
begin
|
||
clipboard.Open;
|
||
try
|
||
Data := GetClipboardData(CF_DIB);
|
||
LoadFromClipboardFormat(CF_DIB, Data, 0);
|
||
finally
|
||
clipboard.Close
|
||
end;
|
||
end
|
||
else
|
||
inherited Assign (source)
|
||
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.AssignFromGraphic |
|
||
| |
|
||
| Assign an TExIcon from another graphic, converting it to our pixel format |
|
||
| and palette. |
|
||
| |
|
||
| Internal use only! |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.AssignFromGraphic (source : TGraphic);
|
||
var
|
||
src, maskBmp : TBitmap;
|
||
offset, infoHeaderSize, imageSize, maskImageSize : DWORD;
|
||
colorBits, maskBits : PChar;
|
||
image : TExIconImage;
|
||
info : PBitmapInfo;
|
||
maskInfo : PBitmapInfo;
|
||
dc : HDC;
|
||
begin
|
||
src := Nil;
|
||
maskBmp := TBitmap.Create;
|
||
|
||
try
|
||
// Get a bitmap with the required format
|
||
src := CreateMappedBitmap (source, Palette, PixelFormat, Width, height);
|
||
|
||
maskBmp.Assign (source); // Get mask bitmap - White where the transparent color
|
||
// occurs - otherwise black.
|
||
|
||
if source is TBitmap then
|
||
maskBmp.Mask (TBitmap (source).transparentColor)
|
||
else
|
||
if Source is TExIconCursor then
|
||
maskBmp.Mask (TExIconCursor(source).transparentColor)
|
||
else
|
||
maskBmp.Mask (clBlack);
|
||
|
||
// Get size for mask bits buffer
|
||
maskImageSize := BytesPerScanLine (Width, 1, 32) * Height;
|
||
|
||
// Get size for color bits buffer
|
||
InternalGetDibSizes (src.Handle, infoHeaderSize, imageSize, PixelFormat);
|
||
|
||
// Create a memory stream to assemble the icon image
|
||
image := TExIconImage.Create;
|
||
try
|
||
image.Reference;
|
||
image.FMemoryImage := TMemoryStream.Create;
|
||
image.FIsIcon := Self is TExIcon;
|
||
|
||
if image.FIsIcon then
|
||
offset := 0
|
||
else
|
||
offset := sizeof (DWORD);
|
||
|
||
image.FMemoryImage.Size := infoHeaderSize + imageSize + maskImageSize + offset;
|
||
|
||
info := PBitmapInfo (PChar (image.FMemoryImage.Memory) + offset);
|
||
colorBits := PChar (info) + infoHeaderSize;
|
||
maskBits := colorBits + imageSize;
|
||
|
||
InternalGetDib (src.Handle, Palette, info, colorBits^, PixelFormat);
|
||
// Get the bitmap header, palette & bits
|
||
|
||
|
||
maskInfo := nil;
|
||
dc := CreateCompatibleDC (0);
|
||
try
|
||
GetMem (maskInfo, SizeOf (TBitmapInfoHeader) + 2 * SizeOf (RGBQUAD));
|
||
// Get mask bits
|
||
|
||
with maskInfo^.bmiHeader do // Set the 1st six members of info header, according
|
||
begin // to the docs.
|
||
|
||
biSize := SizeOf (TBitmapInfoHeader);
|
||
biWidth := Width;
|
||
biHeight := Height;
|
||
biBitCount := 1;
|
||
biPlanes := 1;
|
||
biCompression := BI_RGB;
|
||
end;
|
||
|
||
if GetDIBits (dc, maskBmp.Handle, 0, Height, maskBits, maskInfo^, DIB_RGB_COLORS) = 0 then
|
||
RaiseLastOSError;
|
||
finally
|
||
DeleteDC (dc);
|
||
FreeMem (maskInfo)
|
||
end;
|
||
|
||
MaskBitmapBits (colorBits, PixelFormat, maskBits, Width, Height, Palette);
|
||
|
||
image.FWidth := info^.bmiHeader.biWidth;
|
||
image.FHeight := info^.bmiHeader.biHeight;
|
||
|
||
info^.bmiHeader.biHeight := info^.bmiHeader.biHeight * 2;
|
||
// Adjust height for funky icon Height thing.
|
||
|
||
image.FPixelFormat := src.PixelFormat;
|
||
|
||
image.FGotPalette := False; // ie. we need to get it later if required.
|
||
|
||
if Self is TExCursor then
|
||
PDWORD (image.FMemoryImage.Memory)^ := TExCursor (Self).HotSpot;
|
||
|
||
Images [fCurrentImage].Release;
|
||
fImages [fCurrentImage] := Image;
|
||
Changed (self);
|
||
except
|
||
image.Free;
|
||
raise
|
||
end;
|
||
finally
|
||
maskBmp.Free;
|
||
src.Free
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.AssignTo |
|
||
| |
|
||
| Allow assigning to bitmap |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.AssignTo(dest: TPersistent);
|
||
var
|
||
bmp : TBitmap;
|
||
begin
|
||
if dest is TBitmap then
|
||
begin
|
||
bmp := TBitmap (dest);
|
||
bmp.Assign (nil); // You gotta do this, otherwise transparency goes nuts!
|
||
bmp.PixelFormat := pf24Bit; // Always assign to 24-bit Bitmap so we don't lose colors
|
||
|
||
bmp.Width := Width;
|
||
bmp.Height := Height;
|
||
|
||
bmp.Transparent := True;
|
||
bmp.TransparentColor := TransparentColor;
|
||
bmp.Canvas.Brush.Color := TransparentColor;
|
||
bmp.Canvas.FillRect (RECT (0, 0, Width, Height));
|
||
bmp.Canvas.Draw (0, 0, self);
|
||
end
|
||
else
|
||
inherited AssignTo (dest)
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| constructor TExIconCursor.Create |
|
||
| |
|
||
| Constructor for TExICon |
|
||
*----------------------------------------------------------------------------*)
|
||
constructor TExIconCursor.Create;
|
||
begin
|
||
inherited Create;
|
||
FTransparentColor := RGB ($fe, $e6, $f8);
|
||
SetLength (FImages, 1);
|
||
FImages [0] := TExIconImage.Create;
|
||
FImages [0].FIsIcon := self is TExIcon;
|
||
Images [0].Reference;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| destructor TExIconCursor.Destroy |
|
||
| |
|
||
| destructor for TExIconCursor |
|
||
*----------------------------------------------------------------------------*)
|
||
destructor TExIconCursor.Destroy;
|
||
begin
|
||
ReleaseImages;
|
||
inherited Destroy
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.Draw |
|
||
| |
|
||
| We should be able to do HandleNeeded/DrawIconEx, however we don't want to |
|
||
| call 'HandleNeeded' because of NT bugs, so jump through hoops to draw |
|
||
| direct from the memory image instead. |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.Draw(ACanvas: TCanvas; const Rect: TRect);
|
||
var
|
||
monoBmp, oldMonoBmp : HBITMAP;
|
||
colorBmp, oldColorBmp : HBITMAP;
|
||
colorDC, monoDC, dc : HDC;
|
||
bitsOffset, bitsSize : DWORD;
|
||
info : PBitmapInfo;
|
||
hdr : PBitmapInfoHeader;
|
||
monoInfo : PBitmapInfo;
|
||
bits : PChar;
|
||
|
||
begin
|
||
with fImages [fCurrentImage] do
|
||
if Assigned (fMemoryImage) then
|
||
begin
|
||
info := GetBitmapInfo;
|
||
hdr := @info^.bmiHeader;
|
||
|
||
colorBmp := 0;
|
||
monoBmp := 0;
|
||
oldColorBmp := 0;
|
||
oldMonoBmp := 0;
|
||
monoDC := 0;
|
||
colorDC := 0;
|
||
monoInfo := Nil;
|
||
|
||
dc := GDICheck (GetDC (0));
|
||
try
|
||
hdr^.biHeight := hdr^.biHeight div 2; // Adjust memory image for funky Icon Height thing.
|
||
|
||
GetBitmapInfoSizes (hdr^, bitsOffset, bitsSize, False);
|
||
|
||
// Create Color Bitmap from Color bits & ColorTable
|
||
colorBmp := GDICheck (CreateDIBitmap (dc, info^.bmiHeader, CBM_INIT, PChar (info) + bitsOffset, info^, DIB_RGB_COLORS));
|
||
colorDC := GDICheck (CreateCompatibleDC (0));
|
||
oldColorBmp := GDICheck (SelectObject(colorDC, colorBmp));
|
||
|
||
// Create mono bitmap. For some reason, CreateBitmap
|
||
// creates it upside down if you give it the bits - so
|
||
// you have to do CreateBitmap followed by SetDIBtes
|
||
|
||
GetMem (monoInfo, sizeof (TBitmapInfoHeader) + 2 * sizeof (RGBQUAD));
|
||
Move (hdr^, monoInfo^, sizeof (TBitmapInfoHeader));
|
||
monoInfo^.bmiHeader.biBitCount := 1;
|
||
monoInfo^.bmiHeader.biCompression := 0;
|
||
with PRGBQUAD (PChar (monoInfo) + sizeof (TBitmapInfoHeader) + sizeof (RGBQUAD))^ do
|
||
begin
|
||
rgbRed := $ff;
|
||
rgbGreen := $ff;
|
||
rgbBlue := $ff;
|
||
rgbReserved := 0;
|
||
end;
|
||
|
||
monoBmp := GDICheck (CreateBitmap (hdr^.biWidth, hdr^.biHeight, 1, 1, Nil));
|
||
bits := PChar (info) + bitsOffset + bitsSize;
|
||
monoDC := GDICheck (CreateCompatibleDC (0));
|
||
GDICheck (SetDIBits (monoDC, monoBmp, 0, hdr^.biHeight, bits, monoInfo^, DIB_RGB_COLORS));
|
||
oldMonoBmp := GDICheck (SelectObject(monoDC, monoBmp));
|
||
|
||
// Draw the masked bitmap
|
||
|
||
with rect do TransparentStretchBlt (ACanvas.Handle,
|
||
left, top, right - left, bottom - top,
|
||
colorDC, 0, 0,
|
||
hdr^.biWidth, hdr^.biHeight, monoDC, 0, 0);
|
||
|
||
finally
|
||
hdr^.biHeight := hdr^.biHeight * 2;
|
||
|
||
if oldMonoBmp <> 0 then SelectObject (monoDC, oldMonoBmp);
|
||
if monoDC <> 0 then DeleteDC (monoDC);
|
||
|
||
if oldColorBmp <> 0 then SelectObject (colorDC, oldColorBmp);
|
||
if colorDC <> 0 then DeleteDC (colorDC);
|
||
|
||
if colorBmp <> 0 then DeleteObject (colorBmp);
|
||
if monoBmp <> 0 then DeleteObject (monoBmp);
|
||
ReleaseDC (0, dc);
|
||
if monoInfo <> Nil then FreeMem (monoInfo)
|
||
end
|
||
end
|
||
else
|
||
begin
|
||
|
||
// If you've fed an HICON in directly to the handle property you'll get here.
|
||
// DrawIconEx seems to work - it's CreateIconFromresourceex that blows up...
|
||
|
||
if Handle <> 0 then
|
||
with rect do DrawIconEx (ACanvas.Handle, left, top, Handle, right - left, bottom - top, 0, 0, DI_NORMAL)
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function TExIconCursor.GetEmpty |
|
||
| |
|
||
| Returns true if the TExIconCursor's current image has neither a handle or |
|
||
| an image |
|
||
*----------------------------------------------------------------------------*)
|
||
function TExIconCursor.GetEmpty: Boolean;
|
||
begin
|
||
with FImages [fCurrentImage] do
|
||
Result := (FHandle = 0) and (FMemoryImage = nil);
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function TExIconCursor.GetHandle |
|
||
| |
|
||
| Returns the current image's icon handle. Calls HandleNeeded which may not |
|
||
| be reliable under NT. |
|
||
*----------------------------------------------------------------------------*)
|
||
function TExIconCursor.GetHandle: HICON;
|
||
begin
|
||
HandleNeeded;
|
||
result := Images [fCurrentImage].Handle
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function TExIconCursor.GetHeight |
|
||
| |
|
||
| Returns the current image's height in pixels |
|
||
*----------------------------------------------------------------------------*)
|
||
function TExIconCursor.GetHeight: Integer;
|
||
begin
|
||
result := FImages [fCurrentImage].FHeight;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function TExIconCursor.GetImage |
|
||
| |
|
||
| Get the current image TExIconImage instance |
|
||
*----------------------------------------------------------------------------*)
|
||
function TExIconCursor.GetImage(index: Integer): TExIconImage;
|
||
begin
|
||
result := fImages [index]
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function TExIconCursor.GetImageCount |
|
||
| |
|
||
| Get the nuber of images in the current icon or cursor |
|
||
*----------------------------------------------------------------------------*)
|
||
function TExIconCursor.GetImageCount: Integer;
|
||
begin
|
||
result := Length (fImages);
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function TExIconCursor.GetPalette |
|
||
| |
|
||
| Get the palette handle for the current image |
|
||
*----------------------------------------------------------------------------*)
|
||
function TExIconCursor.GetPalette: HPALETTE;
|
||
begin
|
||
PaletteNeeded;
|
||
result := FImages [fCurrentImage].fPalette;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function TExIconCursor.GetPixelFormat : TPixelFormat |
|
||
| |
|
||
| Get the pixel format for the current image |
|
||
*----------------------------------------------------------------------------*)
|
||
function TExIconCursor.GetPixelFormat: TPixelFormat;
|
||
begin
|
||
result := FImages [fCurrentImage].fPixelFormat
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function TExIconCursor.GetTransparent : boolean |
|
||
| |
|
||
| Overrides TGraphic method to always return TRUE |
|
||
*----------------------------------------------------------------------------*)
|
||
function TExIconCursor.GetTransparent: boolean;
|
||
begin
|
||
result := True
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| function TExIconCursor.GetWidth : Integer |
|
||
| |
|
||
| Returns the current image's width in pixels |
|
||
*----------------------------------------------------------------------------*)
|
||
function TExIconCursor.GetWidth: Integer;
|
||
begin
|
||
result := FImages [fCurrentImage].FWidth;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.HandleNeeded |
|
||
| |
|
||
| Ensure that an HICON handle exists for the current image. Don't use this |
|
||
| unless strictly necessary. It may bugger up in NT4 |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.HandleNeeded;
|
||
begin
|
||
FImages [FCurrentImage].HandleNeeded;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.ImageNeeded |
|
||
| |
|
||
| Ensure that a memory image exists for the current image. |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.ImageNeeded;
|
||
begin
|
||
with FImages [FCurrentImage] do ImageNeeded;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.LoadFromClipboardFormat |
|
||
| |
|
||
| Ensure that a memory image exists for the current image. Affects just the |
|
||
| current image. |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.LoadFromClipboardFormat(AFormat: Word;
|
||
AData: THandle; APalette: HPALETTE);
|
||
var
|
||
Info : PBItmapInfo;
|
||
image : TExIconImage;
|
||
size : DWORD;
|
||
InfoHeaderSize, ImageSize, monoSize : DWORD;
|
||
mask : PByte;
|
||
begin
|
||
size := GlobalSize (AData);
|
||
if (size > 0) and (AFormat = CF_DIB) then
|
||
begin
|
||
|
||
image := TExIconImage.Create;
|
||
image.FMemoryImage := TMemoryStream.Create;
|
||
image.Reference;
|
||
|
||
try
|
||
info := PBitmapInfo (GlobalLock (AData));
|
||
try
|
||
image.FIsIcon := Images [FCurrentImage].FIsIcon;
|
||
|
||
image.FWidth := info^.bmiHeader.biWidth;
|
||
image.FHeight := info^.bmiHeader.biHeight;
|
||
image.FPixelFormat := GetBitmapInfoPixelFormat (info^.bmiHeader);
|
||
|
||
GetBitmapInfoSizes (info^.bmiHeader, InfoHeaderSize, ImageSize, False);
|
||
monoSize := image.Width * image.FHeight div 8;
|
||
|
||
if size = InfoHeaderSize + ImageSize + monoSize then
|
||
image.FMemoryImage.Write (info^, InfoHeaderSize + ImageSize + monoSize)
|
||
else
|
||
begin
|
||
image.FMemoryImage.Write (info^, InfoHeaderSize + ImageSize);
|
||
GetMem (mask, monoSize);
|
||
try
|
||
FillChar (mask^, monoSize, $00);
|
||
image.FMemoryImage.Write (mask^, monoSize)
|
||
finally
|
||
FreeMem (mask)
|
||
end
|
||
end;
|
||
PBitmapInfo (image.FMemoryImage.Memory)^.bmiHeader.biHeight := info^.bmiHeader.biHeight * 2;
|
||
finally
|
||
GlobalUnlock (AData)
|
||
end
|
||
except
|
||
image.Release;
|
||
raise
|
||
end;
|
||
|
||
FImages [FCurrentImage].Release;
|
||
FImages [FCurrentImage] := image
|
||
end
|
||
end;
|
||
|
||
procedure TExIconCursor.LoadFromResourceId(Instance: THandle;
|
||
ResID : Integer);
|
||
var
|
||
Stream: TCustomMemoryStream;
|
||
begin
|
||
Stream := TResourceStream.CreateFromID(Instance, ResID, RT_ICON);
|
||
try
|
||
ReadIcon(Instance, Stream, Stream.Size);
|
||
finally
|
||
Stream.Free;
|
||
end;
|
||
end;
|
||
|
||
procedure TExIconCursor.LoadFromResourceName(Instance: THandle;
|
||
const resName: string);
|
||
var
|
||
Stream: TCustomMemoryStream;
|
||
begin
|
||
Stream := TResourceStream.Create(Instance, ResName, RT_GROUP_ICON);
|
||
try
|
||
ReadIcon(Instance, Stream, Stream.Size);
|
||
finally
|
||
Stream.Free;
|
||
end;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.LoadFromStream |
|
||
| |
|
||
| Load all images from a stream |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.LoadFromStream(Stream: TStream);
|
||
var
|
||
hdr : TIconHeader;
|
||
dirEntry : array of TIconDirEntry;
|
||
i : Integer;
|
||
p : PBitmapInfoHeader;
|
||
begin
|
||
Stream.Read (hdr, SizeOf (hdr));
|
||
|
||
if (self is TExIcon) <> (hdr.wType = 1) then
|
||
raise EInvalidGraphic.Create (rstInvalidIcon);
|
||
|
||
ReleaseImages; // Get rid of existing images
|
||
|
||
SetLength (fImages, hdr.wCount);
|
||
SetLength (dirEntry, hdr.wCount);
|
||
|
||
// Create and initialize the ExIconImage classes and read
|
||
// the dirEntry structures from the stream.
|
||
|
||
for i := 0 to hdr.wCount - 1 do
|
||
begin
|
||
fImages [i] := TExIconImage.Create;
|
||
fImages [i].FIsIcon := self is TExIcon;
|
||
fImages [i].FMemoryImage := TMemoryStream.Create;
|
||
fImages [i].Reference;
|
||
|
||
Stream.Read (dirEntry [i], SizeOf (TIconDirEntry));
|
||
fImages [i].FWidth := dirEntry [i].bWidth;
|
||
fImages [i].FHeight := dirEntry [i].bHeight;
|
||
end;
|
||
|
||
// Read the icon images into their Memory streams
|
||
for i := 0 to hdr.wCount - 1 do
|
||
begin
|
||
|
||
stream.Seek (dirEntry [i].dwImageOffset, soFromBeginning);
|
||
|
||
fImages [i].FMemoryImage.CopyFrom (stream, dirEntry [i].dwBytesInRes);
|
||
|
||
p := FImages [i].GetBitmapInfoHeader;
|
||
p^.biSizeImage := 0;
|
||
|
||
fImages [i].FPixelFormat := GetBitmapInfoPixelFormat (p^);
|
||
end;
|
||
|
||
FCurrentImage := 0;
|
||
Changed(Self);
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.PaletteNeeded |
|
||
| |
|
||
| The palette is needed for the current image |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.PaletteNeeded;
|
||
begin
|
||
FImages [FCurrentImage].PaletteNeeded;
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.ReleaseImages |
|
||
| |
|
||
| Release images for the icon. Internal use only - you must set up at least |
|
||
| one new image after calling it. |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.ReadIcon(instance : THandle; stream: TCustomMemoryStream;
|
||
Size: Integer);
|
||
var
|
||
hdr : TIconHeader;
|
||
resDir : TResourceDirectory;
|
||
i : Integer;
|
||
strm1 : TCustomMemoryStream;
|
||
p : PBitmapInfoHEader;
|
||
begin
|
||
stream.read (hdr, SizeOf (hdr));
|
||
|
||
if (self is TExIcon) <> (hdr.wType = 1) then
|
||
raise EInvalidGraphic.Create (rstInvalidIcon);
|
||
|
||
ReleaseImages; // Get rid of existing images
|
||
|
||
SetLength (fImages, hdr.wCount);
|
||
|
||
for i := 0 to hdr.wCount - 1 do
|
||
begin
|
||
stream.read (resDir, SizeOf (resDir));
|
||
|
||
strm1 := TResourceStream.CreateFromID (Instance, resDir.wNameOrdinal, RT_ICON);
|
||
try
|
||
fImages [i] := TExIconImage.Create;
|
||
fImages [i].FIsIcon := self is TExIcon;
|
||
fImages [i].FMemoryImage := TMemoryStream.Create;
|
||
fImages [i].Reference;
|
||
|
||
if Self is TExIcon then
|
||
begin
|
||
fImages [i].FWidth := resDir.details.iconWidth;
|
||
fImages [i].FHeight := resDir.details.iconHeight
|
||
end
|
||
else
|
||
begin
|
||
fImages [i].FWidth := resDir.details.cursorWidth;
|
||
fImages [i].FHeight := resDir.details.cursorHeight
|
||
end;
|
||
|
||
fImages [i].FMemoryImage.CopyFrom (strm1, 0);
|
||
p := FImages [i].GetBitmapInfoHeader;
|
||
p^.biSizeImage := 0;
|
||
|
||
fImages [i].FPixelFormat := GetBitmapInfoPixelFormat (p^);
|
||
finally
|
||
strm1.Free
|
||
end
|
||
end;
|
||
|
||
FCurrentImage := 0;
|
||
Changed(Self);
|
||
end;
|
||
|
||
function TExIconCursor.ReleaseHandle: HICON;
|
||
begin
|
||
HandleNeeded;
|
||
if FImages [fCurrentImage].RefCount > 1 then
|
||
Result := CopyIcon (FImages [fCurrentImage].FHandle) else
|
||
begin
|
||
Result := FImages [fCurrentImage].FHandle;
|
||
FImages [fCurrentImage].fHandle := 0
|
||
end
|
||
end;
|
||
|
||
procedure TExIconCursor.ReleaseImages;
|
||
var
|
||
i : Integer;
|
||
begin
|
||
for i := 0 to Length (fImages) - 1 do
|
||
fImages [i].Release;
|
||
|
||
SetLength (fImages, 0)
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| TExIconCursor.SaveImageToFile
|
||
| |
|
||
*----------------------------------------------------------------------*)
|
||
procedure TExIconCursor.SaveImageToFile(const FileName: string);
|
||
// Save current image to 'ico' file
|
||
var
|
||
hdr : TIconHeader;
|
||
dirEntry : TIconDirEntry;
|
||
image : TExIconImage;
|
||
dirSize : Integer;
|
||
stream : TStream;
|
||
begin
|
||
hdr.wReserved := 0;
|
||
if not (self is TExCursor) then
|
||
hdr.wType := 1
|
||
else
|
||
hdr.wType := 2;
|
||
hdr.wCount := 1;
|
||
|
||
stream := TFileStream.Create (FileName, fmCreate);
|
||
try
|
||
Stream.Write (hdr, SizeOf (hdr));
|
||
dirSize := sizeof (dirEntry) + sizeof (hdr);
|
||
|
||
ImageNeeded;
|
||
image := Images [CurrentImage];
|
||
|
||
FillChar (dirEntry, SizeOf (dirEntry), 0);
|
||
|
||
dirEntry.bWidth := image.Width;
|
||
dirEntry.bHeight := image.Height;
|
||
|
||
case image.PixelFormat of
|
||
pf1Bit : begin dirEntry.bColorCount := 2; dirEntry.wBitCount := 0; end;
|
||
pf4Bit : begin dirEntry.bColorCount := 16; dirEntry.wBitCount := 0; end;
|
||
pf8Bit : begin dirEntry.bColorCount := 0; dirEntry.wBitCount := 8; end;
|
||
pf16Bit : begin dirEntry.bColorCount := 0; dirEntry.wBitCount := 16; end;
|
||
pf24Bit : begin dirEntry.bColorCount := 0; dirEntry.wBitcount := 24; end;
|
||
pf32Bit : begin dirEntry.bColorCount := 0; dirEntry.wBitCount := 32; end;
|
||
else
|
||
raise EInvalidGraphic.Create (rstInvalidIcon);
|
||
end;
|
||
|
||
if hdr.wType = 2 then
|
||
begin
|
||
dirEntry.wPlanes := LOWORD (TExCursor (Self).Hotspot);
|
||
dirEntry.wBitCount := HIWORD (TExCursor (Self).Hotspot)
|
||
end
|
||
else
|
||
dirEntry.wPlanes := 1;
|
||
dirEntry.dwBytesInRes := image.FMemoryImage.Size;
|
||
if hdr.wType = 2 then
|
||
begin
|
||
image.FMemoryImage.Seek (SizeOf (DWORD), soFromBeginning);
|
||
Dec (dirEntry.dwBytesInRes, SizeOf (DWORD))
|
||
end
|
||
else
|
||
image.FMemoryImage.Seek (0, soFromBeginning);
|
||
|
||
dirEntry.dwImageOffset := dirSize;
|
||
Stream.Write (dirEntry, SizeOf (dirEntry));
|
||
Stream.CopyFrom (image.FMemoryImage, image.FMemoryImage.Size - image.FMemoryImage.Position);
|
||
|
||
finally
|
||
stream.Free
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.SaveToClipboardFormat |
|
||
| |
|
||
| Saves the image on the clipboard as a DDB |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.SaveToClipboardFormat(var AFormat: Word;
|
||
var AData: THandle; var APalette: HPALETTE);
|
||
var
|
||
info : PBitmapInfo;
|
||
InfoHeaderSize, ImageSize, monoSize : DWORD;
|
||
buf : PChar;
|
||
begin
|
||
AFormat := CF_DIB;
|
||
ImageNeeded;
|
||
info := Images [fCurrentImage].GetBitmapInfo;
|
||
info^.bmiHeader.biHeight := info^.bmiHeader.biHeight div 2;
|
||
try
|
||
GetBitmapInfoSizes (info^.bmiHeader, InfoHeaderSize, ImageSize, False);
|
||
monoSize := Width * Height div 8;
|
||
|
||
AData := GlobalAlloc (GMEM_DDESHARE, InfoHeaderSize + ImageSize + monoSize);
|
||
buf := GlobalLock (AData);
|
||
try
|
||
Move (info^, buf^, InfoHeaderSize + ImageSize + monoSize);
|
||
finally
|
||
GlobalUnlock (AData)
|
||
end;
|
||
|
||
APalette := 0; // Don't need the palette, cause we've copied the DIB
|
||
finally
|
||
info^.bmiHeader.biHeight := info^.bmiHeader.biHeight * 2
|
||
end;
|
||
end;
|
||
|
||
procedure TExIconCursor.SaveToStream(Stream: TStream);
|
||
var
|
||
hdr : TIconHeader;
|
||
dirEntry : TIconDirEntry;
|
||
image : TExIconImage;
|
||
i, dirSize, offset : Integer;
|
||
oldCurrentImage : Integer;
|
||
begin
|
||
hdr.wReserved := 0;
|
||
if not (self is TExCursor) then
|
||
hdr.wType := 1
|
||
else
|
||
hdr.wType := 2;
|
||
hdr.wCount := ImageCount;
|
||
|
||
Stream.Write (hdr, SizeOf (hdr));
|
||
dirSize := ImageCount * sizeof (dirEntry) + sizeof (hdr);
|
||
|
||
oldCurrentImage := FCurrentImage;
|
||
try
|
||
offset := 0;
|
||
for i := 0 to ImageCount - 1 do
|
||
begin
|
||
FCurrentImage := i;
|
||
ImageNeeded;
|
||
image := Images [i];
|
||
|
||
FillChar (dirEntry, SizeOf (dirEntry), 0);
|
||
|
||
dirEntry.bWidth := image.Width;
|
||
dirEntry.bHeight := image.Height;
|
||
|
||
case image.PixelFormat of
|
||
pf1Bit : begin dirEntry.bColorCount := 2; dirEntry.wBitCount := 0; end;
|
||
pf4Bit : begin dirEntry.bColorCount := 16; dirEntry.wBitCount := 0; end;
|
||
pf8Bit : begin dirEntry.bColorCount := 0; dirEntry.wBitCount := 8; end;
|
||
pf16Bit : begin dirEntry.bColorCount := 0; dirEntry.wBitCount := 16; end;
|
||
pf24Bit : begin dirEntry.bColorCount := 0; dirEntry.wBitcount := 24; end;
|
||
pf32Bit : begin dirEntry.bColorCount := 0; dirEntry.wBitCount := 32; end;
|
||
else
|
||
raise EInvalidGraphic.Create (rstInvalidIcon);
|
||
end;
|
||
|
||
dirEntry.wPlanes := 1;
|
||
dirEntry.dwBytesInRes := image.FMemoryImage.Size;
|
||
dirEntry.dwImageOffset := dirSize + offset;
|
||
|
||
Stream.Write (dirEntry, SizeOf (dirEntry));
|
||
Inc (offset, dirEntry.dwBytesInRes);
|
||
end
|
||
finally
|
||
FCurrentImage := oldCurrentImage
|
||
end;
|
||
|
||
for i := 0 to ImageCount - 1 do
|
||
images [i].FMemoryImage.SaveToStream (Stream);
|
||
end;
|
||
|
||
procedure TExIconCursor.SetCurrentImage(const Value: Integer);
|
||
begin
|
||
if fCurrentImage <> value then
|
||
begin
|
||
fCurrentImage := Value;
|
||
Changed (self)
|
||
end
|
||
end;
|
||
|
||
procedure TExIconCursor.SetHandle(const Value: HICON);
|
||
var
|
||
iconInfo : TIconInfo;
|
||
BI : TBitmapInfoHeader;
|
||
image : TExIconImage;
|
||
begin
|
||
if GetIconInfo (value, iconInfo) then
|
||
try
|
||
image := TExIconImage.Create;
|
||
try
|
||
InitializeBitmapInfoHeader (iconInfo.hbmColor, BI, pfDevice);
|
||
image.FIsIcon := self is TExIcon;
|
||
image.FWidth := BI.biWidth;
|
||
image.FHeight := BI.biHeight;
|
||
image.FPixelFormat := GetBitmapInfoPixelFormat (BI);
|
||
except
|
||
image.Free;
|
||
raise
|
||
end;
|
||
|
||
image.FHandle := Value;
|
||
|
||
Images [fCurrentImage].Release;
|
||
fImages [fCurrentImage] := image;
|
||
image.Reference;
|
||
Changed(Self)
|
||
finally
|
||
DeleteObject (iconInfo.hbmMask);
|
||
DeleteObject (iconInfo.hbmColor)
|
||
end
|
||
else
|
||
RaiseLastOSError;
|
||
end;
|
||
|
||
procedure TExIconCursor.SetHeight(Value: Integer);
|
||
begin
|
||
if Value = Height then Exit;
|
||
Images [FCurrentImage].FHeight := Value;
|
||
AssignFromGraphic (Self);
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.SetPalette |
|
||
| |
|
||
| Modify the icon so it uses a new palette (with maybe a differnt color |
|
||
| count, hence pixel format... |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.SetPalette(Value: HPALETTE);
|
||
var
|
||
colorCount : DWORD;
|
||
newPixelFormat : TPixelFormat;
|
||
begin
|
||
newPixelFormat := pfDevice;
|
||
colorCount := 0;
|
||
if GetObject (Value, sizeof (colorCount), @colorCount) = 0 then
|
||
RaiseLastOSError;
|
||
|
||
case colorCount of
|
||
1..2 : newPixelFormat := pf1Bit;
|
||
3..16 : newPixelFormat := pf4Bit;
|
||
17..256 : newPixelFormat := pf8Bit;
|
||
end;
|
||
|
||
if FImages [FCurrentImage].FPalette <> 0 then
|
||
DeleteObject (FImages [FCurrentImage].FPalette);
|
||
|
||
if newPixelFormat <> pfDevice then
|
||
begin
|
||
FImages [FCurrentImage].FPixelFormat := newPixelFormat;
|
||
|
||
FImages [FCurrentImage].FPalette := CopyPalette (Value);
|
||
FImages [FCurrentImage].FGotPalette := FImages [FCurrentImage].FPalette <> 0;
|
||
AssignFromGraphic (Self);
|
||
end
|
||
else
|
||
begin
|
||
FImages [FCurrentImage].FPalette := 0;
|
||
FImages [FCurrentImage].FGotPalette := True
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------------*
|
||
| procedure TExIconCursor.SetPixelFormat |
|
||
| |
|
||
| Modify the icon so it uses a new pixel format. If this pixel format has |
|
||
| <= 256 colours, apply an appropriate palette. Could modify this to use |
|
||
| sophisticated color reduction, but at the moment it uses the 'default' |
|
||
| 16 color palete, and the 'netscape' 256 color one. |
|
||
*----------------------------------------------------------------------------*)
|
||
procedure TExIconCursor.SetPixelFormat(const Value: TPixelFormat);
|
||
var
|
||
newPalette : HPALETTE;
|
||
begin
|
||
if value = PixelFormat then Exit;
|
||
|
||
case value of
|
||
pf1Bit : newPalette := SystemPalette2;
|
||
pf4Bit : newPalette := SystemPalette16;
|
||
pf8Bit : newPalette := SystemPalette256;
|
||
else
|
||
newPalette := 0
|
||
end;
|
||
|
||
FImages [FCurrentImage].FPixelFormat := Value;
|
||
|
||
if FImages [FCurrentImage].FPalette <> 0 then
|
||
DeleteObject (FImages [FCurrentImage].FPalette);
|
||
|
||
if newPalette <> 0 then
|
||
begin
|
||
FImages [FCurrentImage].FPalette := CopyPalette (newPalette);
|
||
FImages [FCurrentImage].FGotPalette := FImages [FCurrentImage].FPalette <> 0;
|
||
end
|
||
else
|
||
begin
|
||
FImages [FCurrentImage].FPalette := 0;
|
||
FImages [FCurrentImage].FGotPalette := True
|
||
end;
|
||
|
||
AssignFromGraphic (self)
|
||
end;
|
||
|
||
procedure TExIconCursor.SetWidth (Value: Integer);
|
||
begin
|
||
if Value = Width then Exit;
|
||
|
||
Images [FCurrentImage].FWidth := Value;
|
||
AssignFromGraphic (Self);
|
||
end;
|
||
|
||
{ TExIconImage }
|
||
|
||
destructor TExIconImage.Destroy;
|
||
begin
|
||
FMemoryImage.Free;
|
||
inherited // Which calls FreeHandle if necessary
|
||
end;
|
||
|
||
procedure TExIconImage.FreeHandle;
|
||
begin
|
||
if FHandle <> 0 then
|
||
DestroyIcon(FHandle);
|
||
|
||
if FPalette <> 0 then
|
||
DeleteObject (FPalette);
|
||
FGotPalette := False;
|
||
FPalette := 0;
|
||
FHandle := 0;
|
||
end;
|
||
|
||
function TExIconImage.GetBitmapInfo: PBitmapInfo;
|
||
begin
|
||
if Assigned (FMemoryImage) then
|
||
if FIsIcon then
|
||
result := PBitmapInfo (FMemoryImage.Memory)
|
||
else
|
||
result := PBitmapInfo (PChar (FMemoryImage.Memory) + sizeof (DWORD))
|
||
else
|
||
result := Nil
|
||
end;
|
||
|
||
function TExIconImage.GetBitmapInfoHeader: PBitmapInfoHeader;
|
||
begin
|
||
result := PBitmapInfoHeader (GetBitmapInfo)
|
||
end;
|
||
|
||
function TExIconImage.GetMemoryImage: TCustomMemoryStream;
|
||
begin
|
||
ImageNeeded;
|
||
result := FMemoryImage
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| TExIconImage.HandleNeeded |
|
||
| |
|
||
| In general, call this as little as possible. I don't call it any- |
|
||
| where in this code - I draw the bitmaps directly, rather than using |
|
||
| DrawIconEx, etc. |
|
||
| |
|
||
| CreateIconFromResourceEx is very unreliable with icons > 16 colours |
|
||
*----------------------------------------------------------------------*)
|
||
procedure TExIconImage.HandleNeeded;
|
||
var
|
||
info : PBitmapInfoHeader;
|
||
buff : PByte;
|
||
begin
|
||
if Handle <> 0 then exit;
|
||
if FMemoryImage = Nil then exit;
|
||
|
||
if fPalette <> 0 then
|
||
begin
|
||
DeleteObject (fPalette);
|
||
fPalette := 0;
|
||
fGotPalette := False;
|
||
end;
|
||
|
||
if FMemoryImage.Size > sizeof (TBitmapInfoHeader) + 4 then
|
||
begin
|
||
info := GetBitmapInfoHeader;
|
||
|
||
// Aaaagh. I don't believe I'm doing this. For some reason you cant use 'FMemoryImage.Memory'
|
||
// directly in CreateIconFromResourceEx. You have to copy it to a (GMEM_MOVEABLE) buffer first.
|
||
//
|
||
// And they call NT an operating system!
|
||
|
||
GetMem (buff, FMemoryImage.Size);
|
||
try
|
||
FMemoryImage.Seek (0, soFromBeginning);
|
||
Move (FMemoryImage.Memory^, buff^, FMemoryImage.Size);
|
||
|
||
FHandle := CreateIconFromResourceEx (buff, FMemoryImage.Size, FisIcon, $00030000, info^.biWidth, info^.biHeight div 2, LR_DEFAULTCOLOR);
|
||
finally
|
||
FreeMem (Buff)
|
||
end;
|
||
|
||
if FHandle = 0 then raise
|
||
EInvalidGraphic.Create (rstInvalidIcon);
|
||
|
||
FWidth := info^.biWidth;
|
||
FHeight := info^.biHeight div 2;
|
||
FPixelFormat := GetBitmapInfoPixelFormat (info^);
|
||
|
||
if info^.biBitCount <= 8 then
|
||
FPalette := CreateDIBPalette (PBitmapInfo (info)^);
|
||
|
||
fGotPalette := FPalette <> 0;
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| TExIconImage.ImageNeeded
|
||
| |
|
||
*----------------------------------------------------------------------*)
|
||
procedure TExIconImage.ImageNeeded;
|
||
var
|
||
Image: TMemoryStream;
|
||
IconInfo: TIconInfo;
|
||
MonoInfoSize, ColorInfoSize: DWORD;
|
||
MonoBitsSize, ColorBitsSize: DWORD;
|
||
MonoInfo, MonoBits, ColorInfo, ColorBits: Pointer;
|
||
begin
|
||
if FMemoryImage <> nil then Exit;
|
||
if FHandle = 0 then
|
||
raise EInvalidGraphic.Create (rstInvalidIcon);
|
||
|
||
Image := TMemoryStream.Create;
|
||
try
|
||
GetIconInfo(Handle, IconInfo);
|
||
try
|
||
InternalGetDIBSizes(IconInfo.hbmMask, MonoInfoSize, MonoBitsSize, pf1Bit);
|
||
if IconInfo.hbmColor <> 0 then
|
||
InternalGetDIBSizes(IconInfo.hbmColor, ColorInfoSize, ColorBitsSize, PixelFormat);
|
||
|
||
MonoInfo := nil;
|
||
MonoBits := nil;
|
||
ColorInfo := nil;
|
||
ColorBits := nil;
|
||
try
|
||
MonoInfo := AllocMem(MonoInfoSize);
|
||
MonoBits := AllocMem(MonoBitsSize);
|
||
InternalGetDIB(IconInfo.hbmMask, 0, PBitmapInfo (MonoInfo), MonoBits^, pf1Bit);
|
||
|
||
if IconInfo.hbmColor <> 0 then
|
||
begin
|
||
ColorInfo := AllocMem(ColorInfoSize);
|
||
ColorBits := AllocMem(ColorBitsSize);
|
||
|
||
InternalGetDIB(IconInfo.hbmColor, FPalette, PBitmapInfo (ColorInfo), ColorBits^, PixelFormat);
|
||
with PBitmapInfoHeader(ColorInfo)^ do
|
||
Inc(biHeight, biHeight); { color height includes mono bits }
|
||
end;
|
||
|
||
if (not FIsIcon) then
|
||
begin
|
||
Image.Write (IconInfo.xHotspot, SizeOf (iconInfo.xHotspot));
|
||
Image.Write (IconInfo.yHotspot, SizeOf (iconInfo.yHotspot))
|
||
end;
|
||
|
||
if IconInfo.hbmColor <> 0 then
|
||
begin
|
||
Image.Write(ColorInfo^, ColorInfoSize);
|
||
Image.Write(ColorBits^, ColorBitsSize)
|
||
end
|
||
else
|
||
Image.Write(MonoInfo^, MonoInfoSize);
|
||
|
||
Image.Write(MonoBits^, MonoBitsSize);
|
||
finally
|
||
FreeMem(ColorInfo, ColorInfoSize);
|
||
FreeMem(ColorBits, ColorBitsSize);
|
||
FreeMem(MonoInfo, MonoInfoSize);
|
||
FreeMem(MonoBits, MonoBitsSize);
|
||
end;
|
||
finally
|
||
if IconInfo.hbmColor <> 0 then
|
||
DeleteObject(IconInfo.hbmColor);
|
||
DeleteObject(IconInfo.hbmMask);
|
||
end
|
||
except
|
||
Image.Free;
|
||
raise;
|
||
end;
|
||
FMemoryImage := Image
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| TExIconImage.PaletteNeeded
|
||
| |
|
||
*----------------------------------------------------------------------*)
|
||
procedure TExIconImage.PaletteNeeded;
|
||
var
|
||
info : PBitmapInfoHeader;
|
||
begin
|
||
if fGotPalette then Exit;
|
||
if fMemoryImage = Nil then Exit;
|
||
|
||
info := GetBitmapInfoHeader;
|
||
|
||
if fPixelFormat in [pf1Bit..pf8Bit] then
|
||
FPalette := CreateDIBPalette (PBitmapInfo (info)^);
|
||
|
||
fGotPalette := True;
|
||
end;
|
||
|
||
{ TExCursor }
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| TExCursor.Create
|
||
| |
|
||
*----------------------------------------------------------------------*)
|
||
constructor TExCursor.Create;
|
||
begin
|
||
inherited;
|
||
|
||
with FImages [0] do
|
||
begin
|
||
fWidth := GetSystemMetrics (SM_CXCURSOR);
|
||
fHeight := GetSystemMetrics (SM_CYCURSOR);
|
||
fPixelFormat := pf1Bit
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| TExCursor.GetHotspot
|
||
| |
|
||
*----------------------------------------------------------------------*)
|
||
function TExCursor.GetHotspot: DWORD;
|
||
begin
|
||
ImageNeeded;
|
||
Result := PDWORD (Images [fCurrentImage].FMemoryImage.Memory)^
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| TExCursor.SetHotspot
|
||
| |
|
||
*----------------------------------------------------------------------*)
|
||
procedure TExCursor.LoadFromFile(const FileName: string);
|
||
var
|
||
hdr : TIconHeader;
|
||
dirEntry : array of TIconDirEntry;
|
||
i : Integer;
|
||
p : PBitmapInfoHeader;
|
||
stream : TFileStream;
|
||
hotspot : DWORD;
|
||
begin
|
||
stream := TFileStream.Create (FileName, fmOpenRead or fmShareDenyWrite);
|
||
try
|
||
Stream.Read (hdr, SizeOf (hdr));
|
||
|
||
if hdr.wType <> 2 then
|
||
raise EInvalidGraphic.Create (rstInvalidCursor);
|
||
|
||
ReleaseImages; // Get rid of existing images
|
||
|
||
SetLength (fImages, hdr.wCount);
|
||
SetLength (dirEntry, hdr.wCount);
|
||
|
||
// Create and initialize the ExIconImage classes and read
|
||
// the dirEntry structures from the stream.
|
||
|
||
for i := 0 to hdr.wCount - 1 do
|
||
begin
|
||
fImages [i] := TExIconImage.Create;
|
||
fImages [i].FIsIcon := False;
|
||
fImages [i].FMemoryImage := TMemoryStream.Create;
|
||
fImages [i].Reference;
|
||
|
||
Stream.Read (dirEntry [i], SizeOf (TIconDirEntry));
|
||
fImages [i].FWidth := dirEntry [i].bWidth;
|
||
fImages [i].FHeight := dirEntry [i].bHeight;
|
||
end;
|
||
|
||
// Read the icon images into their Memory streams
|
||
for i := 0 to hdr.wCount - 1 do
|
||
begin
|
||
hotspot := MAKELONG (dirEntry [i].wPlanes, dirEntry [i].wBitCount);
|
||
|
||
stream.Seek (dirEntry [i].dwImageOffset, soFromBeginning);
|
||
|
||
fImages [i].FMemoryImage.Write (hotspot, SizeOf (hotspot));
|
||
fImages [i].FMemoryImage.CopyFrom (stream, dirEntry [i].dwBytesInRes);
|
||
|
||
p := FImages [i].GetBitmapInfoHeader;
|
||
p^.biSizeImage := 0;
|
||
|
||
fImages [i].FPixelFormat := GetBitmapInfoPixelFormat (p^);
|
||
end;
|
||
|
||
FCurrentImage := 0;
|
||
Changed(Self)
|
||
finally
|
||
stream.Free
|
||
end
|
||
end;
|
||
|
||
procedure TExCursor.SaveToFile(const FileName: string);
|
||
var
|
||
hdr : TIconHeader;
|
||
dirEntry : TIconDirEntry;
|
||
image : TExIconImage;
|
||
i, dirSize, offset : Integer;
|
||
oldCurrentImage : Integer;
|
||
stream : TFileStream;
|
||
begin
|
||
stream := TFileStream.Create (FileName, fmCreate);
|
||
try
|
||
hdr.wReserved := 0;
|
||
hdr.wType := 2;
|
||
hdr.wCount := ImageCount;
|
||
|
||
Stream.Write (hdr, SizeOf (hdr));
|
||
dirSize := ImageCount * sizeof (dirEntry) + sizeof (hdr);
|
||
|
||
oldCurrentImage := FCurrentImage;
|
||
try
|
||
offset := 0;
|
||
for i := 0 to ImageCount - 1 do
|
||
begin
|
||
FCurrentImage := i;
|
||
ImageNeeded;
|
||
image := Images [i];
|
||
|
||
FillChar (dirEntry, SizeOf (dirEntry), 0);
|
||
|
||
dirEntry.bWidth := image.Width;
|
||
dirEntry.bHeight := image.Height;
|
||
|
||
case image.PixelFormat of
|
||
pf1Bit : dirEntry.bColorCount := 2;
|
||
pf4Bit : dirEntry.bColorCount := 16;
|
||
pf8Bit : dirEntry.bColorCount := 0;
|
||
pf16Bit : dirEntry.bColorCount := 0;
|
||
pf24Bit : dirEntry.bColorCount := 0;
|
||
pf32Bit : dirEntry.bColorCount := 0;
|
||
else
|
||
raise EInvalidGraphic.Create (rstInvalidIcon);
|
||
end;
|
||
|
||
dirEntry.wPlanes := LOWORD (Hotspot);
|
||
dirEntry.wBitCount := HIWORD (Hotspot);
|
||
|
||
dirEntry.dwBytesInRes := image.FMemoryImage.Size - SizeOf (DWORD);
|
||
dirEntry.dwImageOffset := dirSize + offset;
|
||
|
||
Stream.Write (dirEntry, SizeOf (dirEntry));
|
||
Inc (offset, dirEntry.dwBytesInRes);
|
||
end
|
||
finally
|
||
FCurrentImage := oldCurrentImage
|
||
end;
|
||
|
||
for i := 0 to ImageCount - 1 do
|
||
begin
|
||
fImages [i].FMemoryImage.Seek (SizeOf (DWORD), soFromBeginning);
|
||
Stream.CopyFrom (images [i].FMemoryImage, images [i].FMemoryImage.Size - images [i].fMemoryImage.Position);
|
||
end
|
||
finally
|
||
stream.Free
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| TExCursor.SetHotspot |
|
||
| |
|
||
| Set the cursor's hotspot |
|
||
*----------------------------------------------------------------------*)
|
||
procedure TExCursor.SetHotspot(const Value: DWORD);
|
||
begin
|
||
ImageNeeded;
|
||
PDWORD (images [fCurrentImage].fMemoryImage.memory)^ := Value;
|
||
end;
|
||
|
||
{ TExIcon }
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| TExIcon.Create
|
||
| |
|
||
*----------------------------------------------------------------------*)
|
||
constructor TExIcon.Create;
|
||
begin
|
||
inherited;
|
||
with FImages [0] do
|
||
begin
|
||
fWidth := GetSystemMetrics (SM_CXICON);
|
||
fHeight := GetSystemMetrics (SM_CYICON);
|
||
fPixelFormat := pf4Bit
|
||
end
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| WebPalette
|
||
| |
|
||
*----------------------------------------------------------------------*)
|
||
function WebPalette: HPalette;
|
||
type
|
||
TLogWebPalette = packed record
|
||
palVersion : word;
|
||
palNumEntries : word;
|
||
PalEntries : array [0..5,0..5,0..5] of TPaletteEntry;
|
||
MonoEntries : array [0..23] of TPaletteEntry;
|
||
StdEntries : array [0..15] of TPaletteEntry;
|
||
end;
|
||
var
|
||
r, g, b : byte;
|
||
LogWebPalette : TLogWebPalette;
|
||
LogPalette : TLogpalette absolute LogWebPalette; // Stupid typecast
|
||
begin
|
||
with LogWebPalette do
|
||
begin
|
||
GetPaletteEntries (SystemPalette16, 0, 16, StdEntries);
|
||
palVersion:= $0300;
|
||
palNumEntries:= 256;
|
||
|
||
g := 10;
|
||
for r := 0 to 23 do
|
||
begin
|
||
MonoEntries [r].peRed := g;
|
||
MonoEntries [r].peGreen := g;
|
||
MonoEntries [r].peBlue := g;
|
||
MonoEntries [r].peFlags := 0;
|
||
Inc (g, 10)
|
||
end;
|
||
|
||
for r:=0 to 5 do
|
||
for g:=0 to 5 do
|
||
for b:=0 to 5 do
|
||
begin
|
||
with PalEntries[r,g,b] do
|
||
begin
|
||
peRed := 51 * r;
|
||
peGreen := 51 * g;
|
||
peBlue := 51 * b;
|
||
peFlags := 0;
|
||
end;
|
||
end;
|
||
end;
|
||
Result := CreatePalette(Logpalette);
|
||
end;
|
||
|
||
(*----------------------------------------------------------------------*
|
||
| Create2ColorPalette
|
||
| |
|
||
*----------------------------------------------------------------------*)
|
||
function Create2ColorPalette : HPALETTE;
|
||
const
|
||
palColors2 : array [0..1] of TColor = ($000000, $ffffff);
|
||
var
|
||
logPalette : PLogPalette;
|
||
i, c : Integer;
|
||
|
||
begin
|
||
GetMem (logPalette, sizeof (logPalette) + 2 * sizeof (PALETTEENTRY));
|
||
|
||
try
|
||
logPalette^.palVersion := $300;
|
||
logPalette^.palNumEntries := 2;
|
||
{$R-}
|
||
for i := 0 to 1 do
|
||
with logPalette^.palPalEntry [i] do
|
||
begin
|
||
c := palColors2 [i];
|
||
|
||
peRed := c and $ff;
|
||
peGreen := c shr 8 and $ff;
|
||
peBlue := c shr 16 and $ff
|
||
end;
|
||
{$R+}
|
||
result := CreatePalette (logPalette^);
|
||
finally
|
||
FreeMem (logPalette)
|
||
end
|
||
end;
|
||
|
||
initialization
|
||
SystemPalette256 := WebPalette;
|
||
SystemPalette2 := Create2ColorPalette;
|
||
finalization
|
||
DeleteObject (SystemPalette2);
|
||
DeleteObject (SystemPalette256);
|
||
end.
|