mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-27 18:33:50 +02:00
1027 lines
30 KiB
PHP
1027 lines
30 KiB
PHP
{%MainUnit ../graphics.pp}
|
|
|
|
{******************************************************************************
|
|
TBitMap
|
|
******************************************************************************
|
|
|
|
*****************************************************************************
|
|
* *
|
|
* This file is part of the Lazarus Component Library (LCL) *
|
|
* *
|
|
* See the file COPYING.LCL, included in this distribution, *
|
|
* for details about the copyright. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
|
|
* *
|
|
*****************************************************************************
|
|
}
|
|
|
|
function TestStreamBitmapNativeType(const AStream: TStream): TBitmapNativeType;
|
|
begin
|
|
if TestStreamIsBMP(AStream) then
|
|
Result:=bnWinBitmap
|
|
else if TestStreamIsXPM(AStream) then
|
|
Result:=bnXPixmap
|
|
else if TestStreamIsIcon(AStream) then
|
|
Result := bnIcon
|
|
else
|
|
Result:=bnNone;
|
|
end;
|
|
|
|
function TestStreamIsBMP(const AStream: TStream): boolean;
|
|
var
|
|
Signature: array[0..1] of Char;
|
|
ReadSize: Integer;
|
|
OldPosition: TStreamSeekType;
|
|
begin
|
|
OldPosition:=AStream.Position;
|
|
ReadSize:=AStream.Read(Signature, SizeOf(Signature));
|
|
Result:=(ReadSize=2) and (Signature[0]='B') and (Signature[1]='M');
|
|
//debugln('TestStreamIsBMP ',DbgStr(Signature[0]),' ',DbgStr(Signature[1]));
|
|
AStream.Position:=OldPosition;
|
|
end;
|
|
|
|
procedure TBitMap.Assign(Source: TPersistent);
|
|
var
|
|
SrcBitmap: TBitmap;
|
|
SrcFPImage: TFPCustomImage;
|
|
IntfImage: TLazIntfImage;
|
|
ImgHandle,ImgMaskHandle: HBitmap;
|
|
begin
|
|
if Source=Self then exit;
|
|
if Source is TBitmap then begin
|
|
Changing(Self);
|
|
//DebugLn('TBitMap.Assign ',ClassName,' ',Source.ClassName);
|
|
// TBitmap can share image data
|
|
// -> check if already shared
|
|
SrcBitmap:=TBitmap(Source);
|
|
FTransparent := SrcBitmap.Transparent;
|
|
if SrcBitmap.FImage=FImage then exit;
|
|
|
|
//DebugLn('TBitMap.Assign A RefCount=',FImage.RefCount);
|
|
// image is not shared => new image data
|
|
// -> free canvas (interface handles)
|
|
FreeCanvasContext;
|
|
// release old FImage
|
|
FImage.Release;
|
|
// share FImage with assigned graphic
|
|
FImage:=SrcBitmap.FImage;
|
|
FImage.Reference;
|
|
//DebugLn('TBitMap.Assign B ',Width,',',Height,' ',HandleAllocated,' RefCount=',FImage.RefCount);
|
|
Changed(Self);
|
|
end else if Source is TFPCustomImage then begin
|
|
Changing(Self);
|
|
SrcFPImage:=TFPCustomImage(Source);
|
|
IntfImage:=TLazIntfImage.Create(0,0);
|
|
try
|
|
if HandleAllocated then
|
|
IntfImage.GetDescriptionFromBitmap(Handle)
|
|
else
|
|
IntfImage.GetDescriptionFromDevice(0);
|
|
IntfImage.Assign(SrcFPImage);
|
|
IntfImage.CreateBitmap(ImgHandle,ImgMaskHandle,false);
|
|
Handle:=ImgHandle;
|
|
MaskHandle:=ImgMaskHandle;
|
|
finally
|
|
IntfImage.Free;
|
|
end;
|
|
Changed(Self);
|
|
end else if Source = nil then begin
|
|
FreeSaveStream;
|
|
SetWidthHeight(0,0);
|
|
end else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TBitmap.Draw(DestCanvas: TCanvas; const DestRect: TRect);
|
|
var
|
|
UseMaskHandle: HBitmap;
|
|
SrcDC: hDC;
|
|
DestDC: hDC;
|
|
begin
|
|
if (DestRect.Right<=DestRect.Left) or (DestRect.Bottom<=DestRect.Top)
|
|
or (Width=0) or (Height=0) then exit;
|
|
HandleNeeded;
|
|
if HandleAllocated then begin
|
|
if Transparent then
|
|
UseMaskHandle:=MaskHandle
|
|
else
|
|
UseMaskHandle:=0;
|
|
SrcDC:=Canvas.GetUpdatedHandle([csHandleValid]);
|
|
DestCanvas.Changing;
|
|
DestDC:=DestCanvas.GetUpdatedHandle([csHandleValid]);
|
|
StretchMaskBlt(DestDC,
|
|
DestRect.Left,DestRect.Top,
|
|
DestRect.Right-DestRect.Left,DestRect.Bottom-DestRect.Top,
|
|
SrcDC,0,0,Width,Height, UseMaskHandle,0,0,SRCCOPY);
|
|
DestCanvas.Changed;
|
|
end;
|
|
end;
|
|
|
|
constructor TBitmap.Create;
|
|
begin
|
|
inherited Create;
|
|
FPixelFormat := pfDevice;
|
|
FImage := TBitmapImage.Create;
|
|
FImage.Reference;
|
|
FTransparentColor := clNone;
|
|
end;
|
|
|
|
destructor TBitMap.Destroy;
|
|
begin
|
|
FreeCanvasContext;
|
|
FImage.Release;
|
|
FImage:=nil;
|
|
FreeThenNil(FCanvas);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TBitMap.FreeCanvasContext;
|
|
begin
|
|
if (FCanvas <> nil) then TBitmapCanvas(FCanvas).FreeDC;
|
|
end;
|
|
|
|
function TBitmap.GetCanvas: TCanvas;
|
|
begin
|
|
if FCanvas = nil then
|
|
begin
|
|
HandleNeeded;
|
|
CreateCanvas;
|
|
end;
|
|
Result := FCanvas;
|
|
end;
|
|
|
|
procedure TBitmap.CreateCanvas;
|
|
begin
|
|
if (FCanvas <> nil) or (bmisCreatingCanvas in FInternalState) then exit;
|
|
Include(FInternalState,bmisCreatingCanvas);
|
|
try
|
|
FCanvas := TBitmapCanvas.Create(Self);
|
|
FCanvas.OnChange := @Changed;
|
|
FCanvas.OnChanging := @Changing;
|
|
finally
|
|
Exclude(FInternalState,bmisCreatingCanvas);
|
|
end;
|
|
end;
|
|
|
|
procedure TBitMap.FreeImage;
|
|
begin
|
|
UnshareImage(false);
|
|
Handle := 0;
|
|
end;
|
|
|
|
function TBitmap.HandleAllocated: boolean;
|
|
begin
|
|
Result:=(FImage<>nil) and (FImage.FHandle<>0);
|
|
end;
|
|
|
|
function TBitmap.MaskHandleAllocated: boolean;
|
|
begin
|
|
Result:=(FImage<>nil) and (FImage.FMaskHandle<>0);
|
|
end;
|
|
|
|
function TBitmap.PaletteAllocated: boolean;
|
|
begin
|
|
Result:=FPalette<>0;
|
|
end;
|
|
|
|
procedure TBitmap.CreateFromBitmapHandles(SrcBitmap, SrcMaskBitmap: HBitmap;
|
|
const SrcRect: TRect);
|
|
var
|
|
NewRawImage: TRawImage;
|
|
ImgHandle, ImgMaskHandle: HBitmap;
|
|
begin
|
|
//DebugLn('TBitmap.CreateFromBitmapHandles A SrcRect=',dbgs(SrcRect));
|
|
if not GetRawImageFromBitmap(SrcBitmap,SrcMaskBitmap,SrcRect,NewRawImage) then
|
|
raise EInvalidGraphicOperation.Create('TBitmap.CreateFromBitmapHandles Get RawImage');
|
|
ImgHandle:=0;
|
|
ImgMaskHandle:=0;
|
|
try
|
|
//DebugLn('TBitmap.CreateFromBitmapHandles B SrRect=',dbgs(SrcRect));
|
|
if not CreateBitmapFromRawImage(NewRawImage,ImgHandle,ImgMaskHandle,false) then
|
|
raise EInvalidGraphicOperation.Create('TBitmap.CreateFromBitmapHandles Create bitmaps');
|
|
Handle:=ImgHandle;
|
|
ImgHandle:=0;
|
|
MaskHandle:=ImgMaskHandle;
|
|
ImgMaskHandle:=0;
|
|
finally
|
|
FreeRawImageData(@NewRawImage);
|
|
if ImgHandle<>0 then DeleteObject(ImgHandle);
|
|
if ImgMaskHandle<>0 then DeleteObject(ImgMaskHandle);
|
|
end;
|
|
end;
|
|
|
|
procedure TBitmap.LoadFromDevice(DC: HDC);
|
|
var
|
|
IntfImg: TLazIntfImage;
|
|
ImgHandle, ImgMaskHandle: HBitmap;
|
|
begin
|
|
ImgHandle:=0;
|
|
ImgMaskHandle:=0;
|
|
IntfImg:=nil;
|
|
try
|
|
// create the interface image
|
|
IntfImg:=TLazIntfImage.Create(0,0);
|
|
// get a snapshot
|
|
IntfImg.LoadFromDevice(DC);
|
|
// create HBitmap
|
|
IntfImg.CreateBitmap(ImgHandle,ImgMaskHandle,false);
|
|
// feed HBitmap into a TBitmap
|
|
Handle:=ImgHandle;
|
|
ImgHandle:=0;
|
|
MaskHandle:=ImgMaskHandle;
|
|
ImgMaskHandle:=0;
|
|
finally
|
|
IntfImg.Free;
|
|
if ImgHandle<>0 then DeleteObject(ImgHandle);
|
|
if ImgMaskHandle<>0 then DeleteObject(ImgMaskHandle);
|
|
end;
|
|
end;
|
|
|
|
function TBitmap.LazarusResourceTypeValid(const ResourceType: string): boolean;
|
|
begin
|
|
Result:=((ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TIcon))
|
|
and ((AnsiCompareText(ResourceType,'XPM')=0)
|
|
or (AnsiCompareText(ResourceType,'BMP')=0));
|
|
end;
|
|
|
|
procedure TBitMap.Mask(ATransparentColor: TColor);
|
|
begin
|
|
DebugLn('TBitMap.Mask not implemented');
|
|
end;
|
|
|
|
function TBitmap.GetHandle: HBITMAP;
|
|
begin
|
|
if not FImage.HandleAllocated then
|
|
HandleNeeded;
|
|
Result := FImage.FHandle;
|
|
end;
|
|
|
|
function TBitmap.GetHandleType: TBitmapHandleType;
|
|
begin
|
|
Result:=FImage.GetHandleType;
|
|
end;
|
|
|
|
function TBitmap.GetMaskHandle: HBITMAP;
|
|
begin
|
|
MaskHandleNeeded;
|
|
Result := FImage.FMaskHandle;
|
|
end;
|
|
|
|
procedure TBitmap.SetHandleType(Value: TBitmapHandleType);
|
|
begin
|
|
if HandleType=Value then exit;
|
|
DebugLn('TBitmap.SetHandleType TBitmap.SetHandleType not implemented');
|
|
end;
|
|
|
|
procedure TBitmap.SetMonochrome(const AValue: Boolean);
|
|
begin
|
|
if Monochrome=AValue then exit;
|
|
if AValue then begin
|
|
FreeImage;
|
|
FImage.FDIB.dsbm.bmPlanes := 1;
|
|
FImage.FDIB.dsbm.bmBitsPixel := 1;
|
|
fPixelFormat:=pf1bit;
|
|
end;
|
|
end;
|
|
|
|
procedure TBitmap.SetPixelFormat(const AValue: TPixelFormat);
|
|
begin
|
|
if AValue=PixelFormat then exit;
|
|
FreeImage;
|
|
FPixelFormat:=AValue;
|
|
end;
|
|
|
|
procedure TBitmap.UpdatePixelFormat;
|
|
begin
|
|
FPixelFormat := FImage.GetPixelFormat;
|
|
end;
|
|
|
|
procedure TBitmap.Changed(Sender: TObject);
|
|
begin
|
|
//FMaskBitsValid := False;
|
|
inherited Changed(Sender);
|
|
end;
|
|
|
|
procedure TBitmap.Changing(Sender: TObject);
|
|
// called before the bitmap is modified
|
|
// -> make sure the handle is unshared (otherwise the modifications will also
|
|
// modify all copies)
|
|
begin
|
|
UnshareImage(true);
|
|
FImage.FDIB.dsbmih.biClrUsed := 0;
|
|
FImage.FDIB.dsbmih.biClrImportant := 0;
|
|
FreeSaveStream;
|
|
end;
|
|
|
|
procedure TBitMap.HandleNeeded;
|
|
var
|
|
n : integer;
|
|
UseWidth,
|
|
UseHeight : Longint;
|
|
OldChangeEvent: TNotifyEvent;
|
|
begin
|
|
if (FImage.FHandle <> 0) then exit;
|
|
|
|
// if the bitmap was loaded, create a handle from stream
|
|
if (FImage.FDIBHandle = 0) and (FImage.FSaveStream <> nil) then begin
|
|
FImage.FSaveStream.Position := 0;
|
|
OldChangeEvent := OnChange;
|
|
try
|
|
OnChange := nil;
|
|
LoadFromStream(FImage.FSaveStream); // Current FImage may be destroyed here
|
|
finally
|
|
OnChange := OldChangeEvent;
|
|
end;
|
|
end;
|
|
|
|
// otherwise create a default handle
|
|
if (FImage.FHandle = 0) then begin
|
|
case PixelFormat of
|
|
pfDevice : n:= ScreenInfo.ColorDepth;
|
|
pf1bit : n:= 1;
|
|
pf4bit : n:= 4;
|
|
pf8bit : n:= 8;
|
|
pf15bit : n:= 15;
|
|
pf16bit : n:= 16;
|
|
pf24bit : n:= 24;
|
|
pf32bit : n:= 32;
|
|
else raise EInvalidOperation.Create(rsUnsupportedBitmapFormat);
|
|
end;
|
|
UseWidth := Width;
|
|
UseHeight := Height;
|
|
if UseWidth<1 then UseWidth:=1;
|
|
if UseHeight<1 then UseHeight:=1;
|
|
FImage.FHandle:= CreateBitmap(UseWidth, UseHeight, 1, n, nil);
|
|
//DebugLn('TBitMap.HandleNeeded Self=',DbgS(Self),' FImage.FHandle=',DbgS(FImage.FHandle),' n=',n);
|
|
FImage.FDIB.dsbm.bmWidth := Width;
|
|
FImage.FDIB.dsbm.bmHeight := Height;
|
|
end;
|
|
end;
|
|
|
|
procedure TBitMap.MaskHandleNeeded;
|
|
begin
|
|
//TODO
|
|
end;
|
|
|
|
procedure TBitmap.LoadFromLazarusResource(const ResName: String);
|
|
var
|
|
ms:TMemoryStream;
|
|
res:TLResource;
|
|
begin
|
|
res:=LazarusResources.Find(ResName);
|
|
if (res=nil) or (res.Value='') or not LazarusResourceTypeValid(res.ValueType)
|
|
then exit;
|
|
ms:=TMemoryStream.Create;
|
|
try
|
|
ms.Write(res.Value[1],length(res.Value));
|
|
ms.Position:=0;
|
|
LoadFromStream(ms);
|
|
finally
|
|
ms.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TBitMap.LoadFromStream(Stream: TStream);
|
|
begin
|
|
ReadStream(Stream, true, Stream.Size - Stream.Position);
|
|
end;
|
|
|
|
procedure TBitMap.LoadFromResourceName(Instance: THandle; const ResName: String);
|
|
begin
|
|
DebugLn('ToDo: TBitMap.LoadFromResourceName');
|
|
end;
|
|
|
|
procedure TBitMap.LoadFromResourceID(Instance: THandle; ResID: Integer);
|
|
begin
|
|
DebugLn('ToDo: TBitMap.LoadFromResourceID');
|
|
end;
|
|
|
|
procedure TBitmap.GetSupportedSourceMimeTypes(List: TStrings);
|
|
begin
|
|
if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TIcon) then
|
|
begin
|
|
List.Clear;
|
|
List.Add(PredefinedClipboardMimeTypes[pcfBitmap]);
|
|
List.Add(PredefinedClipboardMimeTypes[pcfDelphiBitmap]);
|
|
List.Add(PredefinedClipboardMimeTypes[pcfPixmap]);
|
|
end else
|
|
inherited GetSupportedSourceMimeTypes(List);
|
|
end;
|
|
|
|
function TBitmap.GetDefaultMimeType: string;
|
|
begin
|
|
if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TIcon) then begin
|
|
if FImage.SaveStream<>nil then begin
|
|
case FImage.SaveStreamType of
|
|
bnXPixmap: Result:=PredefinedClipboardMimeTypes[pcfPixmap];
|
|
else
|
|
Result:=PredefinedClipboardMimeTypes[pcfBitmap];
|
|
end;
|
|
end else
|
|
Result:=PredefinedClipboardMimeTypes[pcfBitmap];
|
|
end else
|
|
Result:=inherited GetDefaultMimeType;
|
|
end;
|
|
|
|
function TBitmap.GetFileExtensions: string;
|
|
begin
|
|
Result:='bmp;xpm';
|
|
end;
|
|
|
|
Procedure TBitmap.LoadFromXPMFile(const Filename: String);
|
|
Begin
|
|
LoadFromFile(Filename);
|
|
end;
|
|
|
|
function TBitmap.GetMonochrome: Boolean;
|
|
begin
|
|
with FImage.FDIB.dsbm do
|
|
Result := (bmPlanes = 1) and (bmBitsPixel = 1);
|
|
end;
|
|
|
|
procedure TBitMap.PaletteNeeded;
|
|
begin
|
|
// ToDo
|
|
end;
|
|
|
|
procedure TBitmap.UnshareImage(CopyContent: boolean);
|
|
var
|
|
NewImage: TBitmapImage;
|
|
OldImage: TBitmapImage;
|
|
IntfImage: TLazIntfImage;
|
|
begin
|
|
if (FImage.RefCount>1) then begin
|
|
//DebugLn('TBitmap.UnshareImage ',ClassName,' ',Width,',',Height,' ',DbgS(Self));
|
|
// release old FImage and create a new one
|
|
NewImage:=TBitmapImage.Create;
|
|
try
|
|
NewImage.Reference;
|
|
if CopyContent and FImage.HandleAllocated
|
|
and (Width>0) and (Height>0) then begin
|
|
// copy content
|
|
IntfImage:=TLazIntfImage.Create(0,0);
|
|
try
|
|
IntfImage.LoadFromBitmap(FImage.FHandle,FImage.FMaskHandle);
|
|
IntfImage.CreateBitmap(NewImage.FHandle,NewImage.FMaskHandle,false);
|
|
FillChar(NewImage.FDIB, SizeOf(NewImage.FDIB), 0);
|
|
if NewImage.HandleAllocated then
|
|
GetObject(NewImage.FHandle, SizeOf(NewImage.FDIB), @NewImage.FDIB);
|
|
finally
|
|
IntfImage.Free;
|
|
end;
|
|
end;
|
|
FreeCanvasContext;
|
|
OldImage:=FImage;
|
|
FImage:=NewImage;
|
|
//DebugLn('TBitMap.UnshareImage Self=',DbgS(Self),' FImage.FHandle=',DbgS(FImage.FHandle));
|
|
NewImage:=nil; // transaction sucessful
|
|
OldImage.Release;
|
|
finally
|
|
// in case something goes wrong, keep old and free new
|
|
NewImage.Free;
|
|
end;
|
|
//DebugLn('TBitmap.UnshareImage END ',ClassName,' ',Width,',',Height,' ',DbgS(Self));
|
|
end;
|
|
end;
|
|
|
|
procedure TBitmap.FreeSaveStream;
|
|
begin
|
|
if FImage.FSaveStream<>nil then begin
|
|
//DebugLn('TBitmap.FreeSaveStream A ',ClassName,' ',FImage.FSaveStream.Size);
|
|
end;
|
|
UnshareImage(false);
|
|
FreeAndNil(FImage.FSaveStream);
|
|
FImage.SaveStreamType:=bnNone;
|
|
FImage.SaveStreamClass:=nil;
|
|
end;
|
|
|
|
procedure TBitmap.ReadStream(Stream: TStream; UseSize: boolean; Size: Longint);
|
|
|
|
procedure RaiseInvalidBitmapHeader;
|
|
begin
|
|
debugln('TBitmap.ReadStream.RaiseInvalidBitmapHeader ',
|
|
'"',dbgMemStream(TCustomMemoryStream(Stream),30),'"');
|
|
raise EInOutError.Create(
|
|
'TBitmap.ReadStream: Invalid bitmap format (bmp,xpm,ico)');
|
|
end;
|
|
|
|
procedure RaiseInvalidSize;
|
|
begin
|
|
debugln('TBitmap.ReadStream.RaiseInvalidSize ',
|
|
' Size=',dbgs(Size),' Stream.Position=',dbgs(Stream.Position),
|
|
' Stream.Size=',dbgs(Stream.Size));
|
|
raise EInOutError.Create(
|
|
'TBitmap.ReadStream: Invalid size of bitmap stream (bmp,xpm,ico)');
|
|
end;
|
|
|
|
var
|
|
CacheStream: TStream;
|
|
StreamType: TBitmapNativeType;
|
|
ReaderClass: TFPCustomImageReaderClass;
|
|
MemStream: TCustomMemoryStream;
|
|
GetSize: Int64;
|
|
OldPosition: Int64;
|
|
begin
|
|
//debugln('TBitmap.ReadStream Stream=',DbgSName(Stream),' Stream.Size=',dbgs(Stream.Size),' Stream.Position=',dbgs(Stream.Position),' UseSize=',dbgs(UseSize),' Size=',dbgs(Size));
|
|
CacheStream:=nil;
|
|
try
|
|
// create mem stream if not already done (to read the image type)
|
|
if (Stream is TCustomMemoryStream) then begin
|
|
CacheStream:=Stream;
|
|
end else if UseSize then begin
|
|
CacheStream:=TMemoryStream.Create;
|
|
TMemoryStream(CacheStream).SetSize(Size);
|
|
CacheStream.CopyFrom(Stream,Size);
|
|
CacheStream.Position:=0;
|
|
end else begin
|
|
// size is unknown and type is not TMemoryStream
|
|
// ToDo: create cache stream from Stream
|
|
CacheStream:=Stream;
|
|
end;
|
|
// get image type
|
|
if CacheStream is TCustomMemoryStream then begin
|
|
MemStream:=TCustomMemoryStream(CacheStream);
|
|
OldPosition:=MemStream.Position;
|
|
GetSize:=MemStream.Size;
|
|
// workaround for TMemoryStream bug, reading Size sets Position to 0
|
|
MemStream.Position:=OldPosition;
|
|
if UseSize and (Size>GetSize-OldPosition) then
|
|
RaiseInvalidSize;
|
|
StreamType:=TestStreamBitmapNativeType(MemStream);
|
|
end else
|
|
StreamType:=bnWinBitmap;
|
|
//debugln('TBitmap.ReadStream ',dbgs(ord(StreamType)),' UseSize=',dbgs(UseSize),' Size=',dbgs(Size),' Stream=',DbgSName(Stream));
|
|
ReaderClass:=nil;
|
|
case StreamType of
|
|
bnWinBitmap: ReaderClass:=TLazReaderBMP;
|
|
bnXPixmap: ReaderClass:=TLazReaderXPM;
|
|
bnIcon: ReaderClass:=TLazReaderIcon;
|
|
else
|
|
RaiseInvalidBitmapHeader;
|
|
end;
|
|
ReadStreamWithFPImage(CacheStream,UseSize,Size,ReaderClass);
|
|
finally
|
|
if CacheStream<>Stream then
|
|
CacheStream.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TBitmap.LoadFromMimeStream(Stream: TStream; const MimeType: string);
|
|
begin
|
|
if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TIcon) then begin
|
|
if (AnsiCompareText(MimeType,PredefinedClipboardMimeTypes[pcfBitmap])=0)
|
|
or (AnsiCompareText(MimeType,PredefinedClipboardMimeTypes[pcfDelphiBitmap])=0)
|
|
or (AnsiCompareText(MimeType,PredefinedClipboardMimeTypes[pcfPixmap])=0) then
|
|
begin
|
|
LoadFromStream(Stream);
|
|
exit;
|
|
end;
|
|
end;
|
|
inherited LoadFromMimeStream(Stream, MimeType);
|
|
end;
|
|
|
|
procedure TBitmap.SetWidthHeight(NewWidth, NewHeight: integer);
|
|
begin
|
|
if (FImage.FDIB.dsbm.bmHeight <> NewHeight)
|
|
or (FImage.FDIB.dsbm.bmWidth <> NewWidth) then
|
|
begin
|
|
FreeImage;
|
|
FImage.FDIB.dsbm.bmWidth := NewWidth;
|
|
FImage.FDIB.dsbm.bmHeight := NewHeight;
|
|
Changed(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TBitmap.WriteStream(Stream: TStream; WriteSize: Boolean);
|
|
begin
|
|
WriteStreamWithFPImage(Stream,WriteSize,nil);
|
|
end;
|
|
|
|
procedure TBitmap.StoreOriginalStream(Stream: TStream; Size: integer);
|
|
var
|
|
MemStream: TMemoryStream;
|
|
begin
|
|
if Stream<>FImage.SaveStream then begin
|
|
MemStream:=TMemoryStream.Create;
|
|
//debugln('TBitmap.StoreOriginalStream Size=',dbgs(Size),' Stream.Position=',dbgs(Stream.Position),' Stream.Size=',dbgs(Stream.Size));
|
|
MemStream.SetSize(Size);
|
|
MemStream.CopyFrom(Stream,Size);
|
|
FreeSaveStream;
|
|
FImage.FSaveStream:=MemStream;
|
|
end;
|
|
FImage.SaveStreamType:=bnNone;
|
|
FImage.SaveStreamClass:=nil;
|
|
FImage.SaveStream.Position:=0;
|
|
end;
|
|
|
|
function TBitmap.CanReadGraphicStreams(AClass: TFPCustomImageWriterClass
|
|
): boolean;
|
|
begin
|
|
Result:=(AClass=GetDefaultFPWriter)
|
|
or (((ClassType=TBitmap) or (ClassType=TPixmap))
|
|
and ((AClass=TFPWriterBMP) or (AClass=TLazWriterXPM)));
|
|
end;
|
|
|
|
{------------------------------------------------------------------------------
|
|
procedure TBitmap.ReadStreamWithFPImage(Stream: TStream; UseSize: boolean;
|
|
Size: Longint; ReaderClass: TFPCustomImageReaderClass);
|
|
|
|
Clear old bitmap and read new bitmap form stream.
|
|
Stream: source stream. After reading Position will be at end of bitmap.
|
|
UseSize: if True, Size is used. If False then Size is calculated
|
|
automatically.
|
|
Size: Only used when UseSize=True. This amount of bytes is read.
|
|
------------------------------------------------------------------------------}
|
|
procedure TBitmap.ReadStreamWithFPImage(Stream: TStream; UseSize: boolean;
|
|
Size: Longint; ReaderClass: TFPCustomImageReaderClass);
|
|
var
|
|
IntfImg: TLazIntfImage;
|
|
ImgReader: TFPCustomImageReader;
|
|
ImgHandle, ImgMaskHandle: HBitmap;
|
|
NewSaveStream: TMemoryStream;
|
|
SrcStream: TStream;
|
|
OldStreamPosition: TStreamSeekType;
|
|
ImgSize: TStreamSeekType;
|
|
|
|
procedure StoreOriginal(OriginalStream: TStream; Size: integer);
|
|
begin
|
|
StoreOriginalStream(OriginalStream,Size);
|
|
NewSaveStream:=FImage.SaveStream;
|
|
NewSaveStream.Position:=0;
|
|
// hide SaveStream during reading (so that it won't be destroyed)
|
|
FImage.SaveStream:=nil;
|
|
end;
|
|
|
|
begin
|
|
//debugln('TBitmap.ReadStreamWithFPImage Stream.Size=',dbgs(Stream.Size),' Stream.Position=',dbgs(Stream.Position),' UseSize=',dbgs(UseSize),' Size=',dbgs(Size));
|
|
|
|
UnshareImage(false);
|
|
if UseSize and (Size = 0) then begin
|
|
FreeSaveStream;
|
|
SetWidthHeight(0,0);
|
|
exit;
|
|
end;
|
|
|
|
IntfImg:=nil;
|
|
ImgReader:=nil;
|
|
NewSaveStream:=nil;
|
|
if UseSize then begin
|
|
// Use the given 'Size' parameter
|
|
StoreOriginal(Stream,Size);
|
|
SrcStream:=NewSaveStream;
|
|
end else begin
|
|
|
|
FreeSaveStream;
|
|
SrcStream:=Stream;
|
|
end;
|
|
try
|
|
// read image
|
|
IntfImg:=TLazIntfImage.Create(0,0);
|
|
IntfImg.GetDescriptionFromDevice(0);
|
|
ImgReader:=ReaderClass.Create;
|
|
InitFPImageReader(ImgReader);
|
|
OldStreamPosition:=SrcStream.Position;
|
|
IntfImg.LoadFromStream(SrcStream,ImgReader);
|
|
ImgSize:=SrcStream.Position-OldStreamPosition;
|
|
if not UseSize then begin
|
|
// now the size is known -> store stream
|
|
//DebugLn('TBitmap.ReadStreamWithFPImage SrcStream=',SrcStream.ClassName,' ImgSize=',ImgSize);
|
|
SrcStream.Position:=OldStreamPosition;
|
|
StoreOriginal(SrcStream,integer(ImgSize));
|
|
end else begin
|
|
// set position
|
|
if Size<>ImgSize then
|
|
SrcStream.Position:=OldStreamPosition+Size;
|
|
end;
|
|
FinalizeFPImageReader(ImgReader);
|
|
IntfImg.CreateBitmap(ImgHandle,ImgMaskHandle,false);
|
|
Handle:=ImgHandle;
|
|
MaskHandle:=ImgMaskHandle;
|
|
finally
|
|
// set save stream
|
|
FImage.SaveStream:=NewSaveStream;
|
|
if ReaderClass=TFPReaderBMP then begin
|
|
FImage.SaveStreamType:=bnWinBitmap;
|
|
FImage.SaveStreamClass:=TFPWriterBMP;
|
|
end else if ReaderClass=TLazReaderXPM then begin
|
|
FImage.SaveStreamType:=bnXPixmap;
|
|
FImage.SaveStreamClass:=TLazWriterXPM;
|
|
end;
|
|
// clean up
|
|
IntfImg.Free;
|
|
ImgReader.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TBitmap.WriteStreamWithFPImage(Stream: TStream; WriteSize: boolean;
|
|
WriterClass: TFPCustomImageWriterClass);
|
|
|
|
Procedure DoWriteStreamSize(DestStream: TStream; Size: longint);
|
|
begin
|
|
//DebugLn('DoWriteStreamSize ',ClassName,' Size=',Size,' WriteSize=',WriteSize);
|
|
if WriteSize then
|
|
DestStream.WriteBuffer(Size, SizeOf(Size));
|
|
end;
|
|
|
|
procedure DoWriteOriginal;
|
|
begin
|
|
DoWriteStreamSize(Stream,longint(FImage.SaveStream.Size));
|
|
FImage.SaveStream.Position:=0;
|
|
if Stream is TMemoryStream then
|
|
TMemoryStream(Stream).SetSize(Stream.Position+FImage.SaveStream.Size);
|
|
Stream.CopyFrom(FImage.SaveStream,FImage.SaveStream.Size);
|
|
end;
|
|
|
|
var
|
|
MemStream: TMemoryStream;
|
|
IntfImg: TLazIntfImage;
|
|
ImgWriter: TFPCustomImageWriter;
|
|
RawImage: TRawImage;
|
|
begin
|
|
//DebugLn('WriteStreamWithFPImage Self=',DbgS(Self),' ',Width,',',Height,' Using SaveStream=',(FImage.SaveStream<>nil) and (FImage.SaveStream.Size>0));
|
|
if (FImage.SaveStream<>nil) and (FImage.SaveStream.Size>0)
|
|
and ((FImage.SaveStreamType<>bnNone)
|
|
or CanReadGraphicStreams(FImage.SaveStreamClass))
|
|
then begin
|
|
// it's a stream format, that this graphic class can read
|
|
// (important for restore)
|
|
DoWriteOriginal;
|
|
exit;
|
|
end;
|
|
//DebugLn('WriteStreamWithFPImage');
|
|
|
|
// write image to temporary stream
|
|
MemStream:=TMemoryStream.Create;
|
|
IntfImg:=nil;
|
|
ImgWriter:=nil;
|
|
try
|
|
IntfImg:=TLazIntfImage.Create(0,0);
|
|
IntfImg.LoadFromBitmap(Handle,FImage.FMaskHandle);
|
|
|
|
if WriterClass=nil then begin
|
|
// automatically use a TFPCustomImageWriterClass
|
|
// .bmp does not support transparency
|
|
// Delphi uses a trick and stores the transparent color in the bottom, left
|
|
// pixel. This changes the bitmap and requires to know the transparency
|
|
// on load.
|
|
// The LCL TBitmap is able to load .xpm and .bmp images. .xpm supports
|
|
// transparency. So, if the images has transparency use .xpm.
|
|
IntfImg.GetRawImage(RawImage);
|
|
if RawImageMaskIsEmpty(@RawImage,true) then
|
|
WriterClass:=TFPWriterBMP
|
|
else
|
|
WriterClass:=TLazWriterXPM;
|
|
//debugln('WriteStreamWithFPImage WriterClass=',WriterClass.ClassName,' ',RawImageDescriptionAsString(@RawImage),' MaskSize=',dbgs(RawImage.MaskSize));
|
|
//debugln(dbgMemRange(RawImage.Mask,RawImage.MaskSize,
|
|
// GetBytesPerLine(RawImage.Description.Width,
|
|
// RawImage.Description.AlphaBitsPerPixel,
|
|
// RawImage.Description.AlphaLineEnd)));
|
|
end;
|
|
|
|
ImgWriter:=WriterClass.Create;
|
|
InitFPImageWriter(ImgWriter);
|
|
IntfImg.SaveToStream(MemStream,ImgWriter);
|
|
FinalizeFPImageWriter(ImgWriter);
|
|
FreeAndNil(ImgWriter);
|
|
FreeAndNil(IntfImg);
|
|
// save stream, so that further saves will be fast
|
|
MemStream.Position:=0;
|
|
FreeSaveStream;
|
|
FImage.SaveStream:=MemStream;
|
|
if WriterClass=TLazWriterXPM then
|
|
FImage.SaveStreamType:=bnXPixmap
|
|
else if WriterClass=TFPWriterBMP then
|
|
FImage.SaveStreamType:=bnWinBitmap
|
|
else
|
|
FImage.SaveStreamType:=bnNone;
|
|
FImage.SaveStreamClass:=WriterClass;
|
|
MemStream:=nil;
|
|
// copy savestream to destination stream
|
|
DoWriteOriginal;
|
|
{SetLength(s,FImage.SaveStream.Size);
|
|
FImage.SaveStream.Position:=0;
|
|
FImage.SaveStream.Read(s[1],length(s));
|
|
DebugLn(s);}
|
|
finally
|
|
MemStream.Free;
|
|
IntfImg.Free;
|
|
ImgWriter.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TBitmap.InitFPImageReader(ImgReader: TFPCustomImageReader);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TBitmap.InitFPImageWriter(ImgWriter: TFPCustomImageWriter);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TBitmap.FinalizeFPImageReader(ImgReader: TFPCustomImageReader);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TBitmap.FinalizeFPImageWriter(ImgWriter: TFPCustomImageWriter);
|
|
begin
|
|
|
|
end;
|
|
|
|
procedure TBitMap.SaveToStream(Stream: TStream);
|
|
begin
|
|
WriteStream(Stream, False);
|
|
end;
|
|
|
|
procedure TBitmap.SetHandle(Value: HBITMAP);
|
|
begin
|
|
if FImage.FHandle = Value then exit;
|
|
// free old handles
|
|
FreeCanvasContext;
|
|
UnshareImage(false);
|
|
FImage.FreeHandle;
|
|
// get the properties from new bitmap
|
|
FImage.FHandle:=Value;
|
|
//DebugLn('TBitMap.SetHandle Self=',DbgS(Self),' FImage.FHandle=',DbgS(FImage.FHandle));
|
|
FillChar(FImage.FDIB, SizeOf(FImage.FDIB), 0);
|
|
if FImage.FHandle <> 0 then
|
|
GetObject(FImage.FHandle, SizeOf(FImage.FDIB), @FImage.FDIB);
|
|
Changed(Self);
|
|
end;
|
|
|
|
procedure TBitmap.SetMaskHandle(NewMaskHandle: HBITMAP);
|
|
// WARNING: Contrary to Delphi, this can change Handle as well. It depends on
|
|
// the widgetset.
|
|
begin
|
|
if FImage.FMaskHandle = NewMaskHandle then exit;
|
|
// unshare image and free canvas handle
|
|
UnshareImage(true);
|
|
FreeCanvasContext;
|
|
// create handle
|
|
HandleNeeded;
|
|
// free old mask handle
|
|
FImage.FreeMaskHandle;
|
|
// combine (depending on the interface we will end with one or two handles)
|
|
{$IFDEF VerboseImgMasks}
|
|
DebugLn('TBitmap.SetMaskHandle Before Replace FImage.FHandle=',DbgS(FImage.FHandle),
|
|
' FImage.FMaskHandle=',DbgS(FImage.FMaskHandle),
|
|
' NewMaskHandle=',DbgS(NewMaskHandle));
|
|
{$ENDIF}
|
|
ReplaceBitmapMask(FImage.FHandle,FImage.FMaskHandle,NewMaskHandle);
|
|
FTransparent := FImage.FMaskHandle <> 0;
|
|
{$IFDEF VerboseImgMasks}
|
|
DebugLn('TBitmap.SetMaskHandle After Replace FImage.FHandle=',DbgS(FImage.FHandle),
|
|
' FImage.FMaskHandle=',DbgS(FImage.FMaskHandle),
|
|
' NewMaskHandle=',DbgS(NewMaskHandle));
|
|
{$ENDIF}
|
|
Changed(Self);
|
|
end;
|
|
|
|
// release handles without freeing them
|
|
// useful for creating a HBitmap
|
|
Function TBitmap.ReleaseHandle: HBITMAP;
|
|
Begin
|
|
//HandleNeeded; Delphi creates a handle. Why?
|
|
FreeCanvasContext;
|
|
Result := FImage.ReleaseHandle;
|
|
end;
|
|
|
|
function TBitmap.ReleasePalette: HPALETTE;
|
|
begin
|
|
// ToDo
|
|
Result := 0;
|
|
end;
|
|
|
|
function TBitmap.GetFPReaderForFileExt(const FileExtension: string
|
|
): TFPCustomImageReaderClass;
|
|
begin
|
|
Result:=nil;
|
|
if (AnsiCompareText(ClassName,'TBitmap')=0)
|
|
or (AnsiCompareText(ClassName,'TPixmap')=0) then begin
|
|
if (AnsiCompareText(FileExtension,'.bmp')=0)
|
|
or (AnsiCompareText(FileExtension,'bmp')=0) then
|
|
Result:=TLazReaderBMP
|
|
else if (AnsiCompareText(FileExtension,'.xpm')=0)
|
|
or (AnsiCompareText(FileExtension,'xpm')=0) then
|
|
Result:=TLazReaderXPM;
|
|
end;
|
|
end;
|
|
|
|
function TBitmap.GetFPWriterForFileExt(const FileExtension: string
|
|
): TFPCustomImageWriterClass;
|
|
begin
|
|
Result:=nil;
|
|
if (AnsiCompareText(ClassName,'TBitmap')=0)
|
|
or (AnsiCompareText(ClassName,'TPixmap')=0) then begin
|
|
if (AnsiCompareText(FileExtension,'.bmp')=0)
|
|
or (AnsiCompareText(FileExtension,'bmp')=0) then
|
|
Result:=TFPWriterBMP
|
|
else if (AnsiCompareText(FileExtension,'.xpm')=0)
|
|
or (AnsiCompareText(FileExtension,'xpm')=0) then
|
|
Result:=TLazWriterXPM;
|
|
end;
|
|
end;
|
|
|
|
function TBitmap.GetDefaultFPReader: TFPCustomImageReaderClass;
|
|
begin
|
|
if (AnsiCompareText(ClassName,'TBitmap')=0) then
|
|
Result:=TLazReaderBMP
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TBitmap.GetDefaultFPWriter: TFPCustomImageWriterClass;
|
|
begin
|
|
if ClassType=TBitmap then
|
|
Result:=TFPWriterBMP
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
procedure TBitmap.WriteNativeStream(Stream: TStream; WriteSize: Boolean;
|
|
SaveStreamType: TBitmapNativeType);
|
|
var
|
|
Writer: TFPCustomImageWriterClass;
|
|
begin
|
|
case SaveStreamType of
|
|
bnWinBitmap: Writer:=TFPWriterBMP;
|
|
bnXPixmap: Writer:=TLazWriterXPM;
|
|
else
|
|
RaiseGDBException('Invalid SaveStreamType');
|
|
end;
|
|
WriteStreamWithFPImage(Stream,WriteSize,Writer);
|
|
if (FImage.SaveStream<>nil) and (FImage.SaveStreamType=bnNone) then begin
|
|
FImage.SaveStreamType:=SaveStreamType;
|
|
FImage.SaveStreamClass:=Writer;
|
|
end;
|
|
end;
|
|
|
|
function TBitmap.CreateIntfImage: TLazIntfImage;
|
|
begin
|
|
Result:=TLazIntfImage.Create(0,0);
|
|
Result.LoadFromBitmap(Handle,MaskHandle);
|
|
end;
|
|
|
|
function TBitmap.GetEmpty: boolean;
|
|
begin
|
|
Result:=FImage.IsEmpty;
|
|
end;
|
|
|
|
function TBitmap.GetHeight: Integer;
|
|
begin
|
|
Result := FImage.FDIB.dsbm.bmHeight;
|
|
end;
|
|
|
|
function TBitmap.GetPalette: HPALETTE;
|
|
begin
|
|
Result:=inherited GetPalette;
|
|
end;
|
|
|
|
function TBitmap.GetWidth: Integer;
|
|
begin
|
|
Result := FImage.FDIB.dsbm.bmWidth;
|
|
end;
|
|
|
|
procedure TBitmap.ReadData(Stream: TStream);
|
|
var
|
|
Size: Longint;
|
|
begin
|
|
Stream.Read(Size, SizeOf(Size));
|
|
Size := LEtoN(Size);
|
|
ReadStream(Stream, true, Size);
|
|
end;
|
|
|
|
procedure TBitmap.WriteData(Stream: TStream);
|
|
begin
|
|
WriteStream(Stream, True);
|
|
end;
|
|
|
|
procedure TBitmap.SetWidth(NewWidth: Integer);
|
|
begin
|
|
SetWidthHeight(NewWidth,Height);
|
|
end;
|
|
|
|
procedure TBitmap.SetHeight(NewHeight: Integer);
|
|
begin
|
|
SetWidthHeight(Width,NewHeight);
|
|
end;
|
|
|
|
procedure TBitmap.SetPalette(Value: HPALETTE);
|
|
begin
|
|
inherited SetPalette(Value);
|
|
end;
|
|
|
|
procedure TBitmap.SetTransparentMode(Value: TTransparentMode);
|
|
begin
|
|
if Value=TransparentMode then exit;
|
|
DebugLn('Note: TBitmap.SetTransparentMode not implemented');
|
|
end;
|
|
|
|
// included by graphics.pp
|
|
|
|
|