mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 08:37:58 +02:00
982 lines
25 KiB
PHP
982 lines
25 KiB
PHP
{%MainUnit ../graphics.pp}
|
|
|
|
{******************************************************************************
|
|
TRasterImage
|
|
******************************************************************************
|
|
|
|
*****************************************************************************
|
|
This file is part of the Lazarus Component Library (LCL)
|
|
|
|
See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
for details about the license.
|
|
*****************************************************************************
|
|
}
|
|
|
|
procedure TRasterImage.Assign(Source: TPersistent);
|
|
procedure CopyMask(AMask: HBITMAP);
|
|
var
|
|
RI: TRawImage;
|
|
msk, dummy: HBITMAP;
|
|
Res: Boolean;
|
|
begin
|
|
// we need a winapi.CopyImage here (would make things easier)
|
|
// in theory, it should not matter if a HBITMAP was created as bitmap or as mask
|
|
// since there is a description problem in gtk, create both (we cannot create mask only)
|
|
// todo: fix gtk
|
|
if not RawImage_FromBitmap(RI, AMask, AMask) then Exit;
|
|
msk := 0;
|
|
dummy := 0;
|
|
RawImage_CreateBitmaps(RI, dummy, msk {, True});
|
|
RI.FreeData;
|
|
DeleteObject(dummy);
|
|
|
|
if BitmapHandleAllocated
|
|
then Res := UpdateHandles(BitmapHandle, msk)
|
|
else Res := UpdateHandles(0, msk);
|
|
if not Res then DeleteObject(msk);
|
|
end;
|
|
|
|
|
|
var
|
|
SrcImage: TRasterImage absolute Source;
|
|
SrcFPImage: TFPCustomImage absolute Source;
|
|
SrcRawImage, DstRawImage: PRawImage;
|
|
IntfImage: TLazIntfImage;
|
|
ImgHandle,ImgMaskHandle: HBitmap;
|
|
begin
|
|
if Source = Self then exit;
|
|
|
|
if Source is TRasterImage
|
|
then begin
|
|
if MaskHandleAllocated
|
|
then begin
|
|
// Clear mask first mask
|
|
if BitmapHandleAllocated
|
|
then UpdateHandles(BitmapHandle, 0)
|
|
else UpdateHandles(0, 0);
|
|
end;
|
|
|
|
FTransparentMode := SrcImage.FTransparentMode;
|
|
FTransparentColor := SrcImage.FTransparentColor;
|
|
FMasked := SrcImage.FMasked;
|
|
|
|
// -> check if already shared
|
|
if SrcImage.FSharedImage <> FSharedImage
|
|
then begin
|
|
// image is not shared => new image data
|
|
// -> free canvas (interface handles)
|
|
FreeCanvasContext;
|
|
// release old FImage
|
|
FSharedImage.Release;
|
|
|
|
// We only can share images of the same type ...
|
|
if CanShareImage(SrcImage.GetSharedImageClass)
|
|
then begin
|
|
// share FImage with assigned graphic
|
|
FSharedImage := SrcImage.FSharedImage;
|
|
FSharedImage.Reference;
|
|
|
|
// when updating, unshare
|
|
// Since we "share" it first, the unshare code will create a copy
|
|
if (FUpdateCount > 0)
|
|
or (SrcImage.FUpdateCount > 0)
|
|
then begin
|
|
UnshareImage(True);
|
|
FreeSaveStream;
|
|
end;
|
|
end
|
|
else begin
|
|
// not sharable, create rawimage copy
|
|
FSharedImage := GetSharedImageClass.Create;
|
|
FSharedImage.Reference;
|
|
|
|
// copy raw image
|
|
SrcRawImage := SrcImage.GetRawImagePtr;
|
|
DstRawImage := GetRawImagePtr;
|
|
if (SrcRawImage <> nil) and (DstRawImage <> nil)
|
|
then with SrcRawImage^ do
|
|
ExtractRect(Rect(0, 0, Description.Width, Description.Height), DstRawImage^);
|
|
end;
|
|
end;
|
|
|
|
if SrcImage.MaskHandleAllocated
|
|
then CopyMask(SrcImage.MaskHandle);
|
|
|
|
|
|
if FUpdateCount = 0
|
|
then Changed(Self);
|
|
|
|
Exit;
|
|
end;
|
|
|
|
if Source is TFPCustomImage
|
|
then begin
|
|
// todo: base on rawimage
|
|
|
|
IntfImage := TLazIntfImage.Create(0,0,[]);
|
|
try
|
|
if BitmapHandleAllocated
|
|
then IntfImage.DataDescription := GetDescriptionFromBitmap(BitmapHandle, 0, 0)
|
|
else IntfImage.DataDescription := GetDescriptionFromDevice(0, 0, 0);
|
|
IntfImage.Assign(SrcFPImage);
|
|
IntfImage.CreateBitmaps(ImgHandle, ImgMaskHandle);
|
|
SetHandles(ImgHandle, ImgMaskHandle);
|
|
finally
|
|
IntfImage.Free;
|
|
end;
|
|
if FUpdateCount = 0
|
|
then Changed(Self);
|
|
|
|
Exit;
|
|
end;
|
|
|
|
// fall back to default
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TRasterImage.BeginUpdate(ACanvasOnly: Boolean);
|
|
begin
|
|
if FUpdateCount = 0
|
|
then begin
|
|
UnshareImage(True);
|
|
FUpdateCanvasOnly := ACanvasOnly;
|
|
end
|
|
else begin
|
|
// if we are updating all, then requesting a canvas only won't change it
|
|
// if we are updating canvas only, then requesting all is an error
|
|
if FUpdateCanvasOnly and not ACanvasOnly
|
|
then raise EInvalidGraphicOperation.Create(rsRasterImageUpdateAll);
|
|
end;
|
|
|
|
Inc(FUpdateCount);
|
|
end;
|
|
|
|
procedure TRasterImage.Clear;
|
|
begin
|
|
if Empty then Exit;
|
|
|
|
FreeSaveStream;
|
|
SetSize(0, 0);
|
|
if FUpdateCount = 0
|
|
then Changed(Self);
|
|
end;
|
|
|
|
procedure TRasterImage.BitmapHandleNeeded;
|
|
var
|
|
ImgHandle, ImgMaskHandle: HBitmap;
|
|
ImagePtr: PRawImage;
|
|
DevDesc: TRawImageDescription;
|
|
QueryFlags: TRawImageQueryFlags;
|
|
W, H: Integer;
|
|
SkipMask: Boolean;
|
|
begin
|
|
if BitmapHandleAllocated then exit;
|
|
|
|
ImagePtr := GetRawImagePtr;
|
|
if ImagePtr = nil then Exit;
|
|
|
|
ImgMaskHandle := 0;
|
|
|
|
// we must skip mask creation if
|
|
// a) we already have mask
|
|
// b) mask needs to be created another way - using TransparentColor
|
|
// c) there's no mask data
|
|
SkipMask := MaskHandleAllocated
|
|
or (TransparentMode = tmFixed)
|
|
or not ImagePtr^.IsMasked(False);
|
|
|
|
if not CreateCompatibleBitmaps(ImagePtr^, ImgHandle, ImgMaskHandle, SkipMask)
|
|
then begin
|
|
{$IFNDEF DisableChecks}
|
|
DebugLn('TRasterImage.BitmapHandleNeeded: Unable to create handles, using default');
|
|
{$ENDIF}
|
|
// create a default handle
|
|
W := Width;
|
|
if W < 1 then W := 1;
|
|
H := Height;
|
|
if H < 1 then H := 1;
|
|
|
|
if ImagePtr^.Description.Depth = 1
|
|
then QueryFlags := [riqfMono]
|
|
else QueryFlags := [riqfRGB];
|
|
if ImagePtr^.Description.AlphaPrec <> 0
|
|
then Include(QueryFlags, riqfAlpha);
|
|
if ImagePtr^.Description.MaskBitsPerPixel <> 0
|
|
then Include(QueryFlags, riqfMask);
|
|
QueryDescription(DevDesc, QueryFlags, W, H);
|
|
ImgHandle := CreateDefaultBitmapHandle(DevDesc);
|
|
end;
|
|
|
|
// if we do not have new mask then either use old one or use none
|
|
if SkipMask and MaskHandleAllocated
|
|
then UpdateHandles(ImgHandle, MaskHandle)
|
|
else UpdateHandles(ImgHandle, ImgMaskHandle);
|
|
end;
|
|
|
|
function TRasterImage.CanShareImage(AClass: TSharedRasterImageClass): Boolean;
|
|
begin
|
|
// We only can share images of the same type.
|
|
// And if we are some "end" graphic type
|
|
Result := (AClass <> TSharedCustomBitmap)
|
|
and (AClass = GetSharedImageClass);
|
|
end;
|
|
|
|
|
|
procedure TRasterImage.Draw(DestCanvas: TCanvas; const DestRect: TRect);
|
|
var
|
|
UseMaskHandle: HBitmap;
|
|
SrcDC: hDC;
|
|
DestDC: hDC;
|
|
begin
|
|
if (Width=0) or (Height=0)
|
|
then Exit;
|
|
|
|
BitmapHandleNeeded;
|
|
if not BitmapHandleAllocated then Exit;
|
|
|
|
if Masked 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,DestCanvas.CopyMode);
|
|
DestCanvas.Changed;
|
|
end;
|
|
|
|
procedure TRasterImage.EndUpdate(AStreamIsValid: Boolean = False);
|
|
begin
|
|
if FUpdatecount = 0
|
|
then raise EInvalidGraphicOperation.Create(rsRasterImageEndUpdate);
|
|
|
|
Dec(FUpdatecount);
|
|
if FUpdatecount > 0 then Exit;
|
|
|
|
if not FUpdateCanvasOnly
|
|
then begin
|
|
FreeCanvasContext;
|
|
// delete bitmaphandle too
|
|
if BitmapHandleAllocated
|
|
then DeleteObject(InternalReleaseBitmapHandle)
|
|
|
|
end;
|
|
|
|
if not AStreamIsValid
|
|
then FreeSaveStream;
|
|
Changed(Self);
|
|
end;
|
|
|
|
constructor TRasterImage.Create;
|
|
begin
|
|
inherited Create;
|
|
FSharedImage := GetSharedImageClass.Create;
|
|
FSharedImage.Reference;
|
|
FTransparentColor := clDefault; // for Delphi compatibility. clDefault means:
|
|
// use Left,Bottom pixel as transparent pixel
|
|
end;
|
|
|
|
destructor TRasterImage.Destroy;
|
|
begin
|
|
FreeCanvasContext;
|
|
FSharedImage.Release;
|
|
FSharedImage := nil;
|
|
FreeAndNil(FCanvas);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TRasterImage.FreeCanvasContext;
|
|
begin
|
|
if (FCanvas <> nil) then TBitmapCanvas(FCanvas).FreeDC;
|
|
end;
|
|
|
|
function TRasterImage.GetCanvas: TCanvas;
|
|
begin
|
|
if FCanvas = nil then
|
|
CreateCanvas;
|
|
Result := FCanvas;
|
|
end;
|
|
|
|
procedure TRasterImage.CreateCanvas;
|
|
begin
|
|
if FCanvas <> nil then Exit;
|
|
|
|
FCanvas := TBitmapCanvas.Create(Self);
|
|
FCanvas.OnChanging := @CanvasChanging;
|
|
FCanvas.OnChange := @Changed;
|
|
end;
|
|
|
|
procedure TRasterImage.FreeImage;
|
|
begin
|
|
SetHandle(0);
|
|
end;
|
|
|
|
procedure TRasterImage.LoadFromBitmapHandles(ABitmap, AMask: HBitmap; ARect: PRect);
|
|
var
|
|
RawImg: TRawImage;
|
|
ImgHandle, ImgMaskHandle: HBitmap;
|
|
begin
|
|
//DebugLn('TRasterImage.CreateFromBitmapHandles A SrcRect=',dbgs(SrcRect));
|
|
if not RawImage_FromBitmap(RawImg, ABitmap, AMask, ARect) then
|
|
raise EInvalidGraphicOperation.Create('TRasterImage.LoadFromBitmapHandles Get RawImage');
|
|
ImgHandle:=0;
|
|
ImgMaskHandle:=0;
|
|
try
|
|
//DebugLn('TRasterImage.CreateFromBitmapHandles B SrRect=',dbgs(SrcRect));
|
|
if not RawImage_CreateBitmaps(RawImg, ImgHandle, ImgMaskHandle) then
|
|
raise EInvalidGraphicOperation.Create('TRasterImage.LoadFromBitmapHandles Create bitmaps');
|
|
SetHandles(ImgHandle, ImgMaskHandle);
|
|
ImgHandle:=0;
|
|
ImgMaskHandle:=0;
|
|
finally
|
|
RawImg.FreeData;
|
|
if ImgHandle<>0 then DeleteObject(ImgHandle);
|
|
if ImgMaskHandle<>0 then DeleteObject(ImgMaskHandle);
|
|
end;
|
|
end;
|
|
|
|
procedure TRasterImage.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.CreateBitmaps(ImgHandle, ImgMaskHandle);
|
|
// feed HBitmap into a TRasterImage
|
|
SetHandles(ImgHandle, ImgMaskHandle);
|
|
ImgHandle:=0;
|
|
ImgMaskHandle:=0;
|
|
finally
|
|
IntfImg.Free;
|
|
if ImgHandle<>0 then DeleteObject(ImgHandle);
|
|
if ImgMaskHandle<>0 then DeleteObject(ImgMaskHandle);
|
|
end;
|
|
end;
|
|
|
|
procedure TRasterImage.Mask(ATransparentColor: TColor);
|
|
begin
|
|
CreateMask(ATransparentColor);
|
|
end;
|
|
|
|
procedure TRasterImage.SetTransparentColor(AValue: TColor);
|
|
begin
|
|
if FTransparentColor = AValue then exit;
|
|
FTransparentColor := AValue;
|
|
|
|
if AValue = clDefault
|
|
then FTransparentMode := tmAuto
|
|
else FTransparentMode := tmFixed;
|
|
|
|
if MaskHandleAllocated
|
|
then MaskHandle := 0
|
|
else Changed(Self);
|
|
end;
|
|
|
|
procedure TRasterImage.Changed(Sender: TObject);
|
|
begin
|
|
if FUpdateCount > 0 then Exit;
|
|
|
|
//FMaskBitsValid := False;
|
|
if Sender = FCanvas
|
|
then FreeSaveStream;
|
|
|
|
inherited Changed(Sender);
|
|
end;
|
|
|
|
function TRasterImage.CreateDefaultBitmapHandle(
|
|
const ADesc: TRawImageDescription): HBITMAP;
|
|
begin
|
|
Result := 0;
|
|
end;
|
|
|
|
procedure TRasterImage.CanvasChanging(Sender: TObject);
|
|
begin
|
|
if FUpdateCount > 0 then Exit;
|
|
|
|
// called before the canvas is modified
|
|
// -> make sure the handle is unshared (otherwise the modifications will also
|
|
// modify all copies)
|
|
// -> Savestream will be freed when changed (so it can be loaded by canvas)
|
|
UnshareImage(True);
|
|
end;
|
|
|
|
procedure TRasterImage.LoadFromStream(AStream: TStream);
|
|
begin
|
|
LoadFromStream(AStream, AStream.Size - AStream.Position);
|
|
end;
|
|
|
|
procedure TRasterImage.LoadFromStream(AStream: TStream; ASize: Cardinal);
|
|
var
|
|
WorkStream: TMemoryStream;
|
|
OldPos, NewSize: Int64;
|
|
begin
|
|
BeginUpdate;
|
|
UnshareImage(False);
|
|
Clear; // clear old saved stream, allocated handles, etc
|
|
if ASize = 0
|
|
then begin
|
|
EndUpdate(False);
|
|
Exit;
|
|
end;
|
|
|
|
WorkStream := nil;
|
|
try
|
|
WorkStream := TMemoryStream.Create;
|
|
WorkStream.SetSize(ASize);
|
|
OldPos := AStream.Position;
|
|
WorkStream.CopyFrom(AStream, ASize);
|
|
WorkStream.Position := 0;
|
|
ReadStream(WorkStream, ASize);
|
|
NewSize := WorkStream.Position;
|
|
if NewSize < ASize
|
|
then begin
|
|
// the size given was different than the image loaded
|
|
// MWE: original code adjusted the streampos, do we stil need this ?
|
|
AStream.Position := OldPos + NewSize;
|
|
WorkStream.SetSize(NewSize);
|
|
end;
|
|
// Store our worstream as savestream
|
|
FSharedImage.SaveStream := WorkStream;
|
|
WorkStream := nil;
|
|
finally
|
|
// if something went wrong, free the workstream
|
|
WorkStream.Free;
|
|
end;
|
|
EndUpdate(True);
|
|
end;
|
|
|
|
function TRasterImage.GetRawImage: TRawImage;
|
|
var
|
|
p: PRawImage;
|
|
begin
|
|
p := GetRawImagePtr;
|
|
if p = nil
|
|
then Result{%H-}.Init
|
|
else Result := p^;
|
|
end;
|
|
|
|
function TRasterImage.GetScanline(ARow: Integer): Pointer;
|
|
var
|
|
RI: TRawImage;
|
|
begin
|
|
RI := GetRawImage;
|
|
Result := RI.GetLineStart(ARow);
|
|
end;
|
|
|
|
function TRasterImage.GetTransparentColor: TColor;
|
|
begin
|
|
if FTransparentColor = clDefault
|
|
then Result := RequestTransparentColor
|
|
else Result := FTransparentColor;
|
|
end;
|
|
|
|
procedure TRasterImage.GetSupportedSourceMimeTypes(List: TStrings);
|
|
begin
|
|
if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TCustomIcon) then
|
|
begin
|
|
List.Clear;
|
|
List.Add(PredefinedClipboardMimeTypes[pcfBitmap]);
|
|
List.Add(PredefinedClipboardMimeTypes[pcfPixmap]);
|
|
end else
|
|
inherited GetSupportedSourceMimeTypes(List);
|
|
end;
|
|
|
|
function TRasterImage.GetTransparent: Boolean;
|
|
var
|
|
Desc: PRawImageDescription;
|
|
begin
|
|
if Masked
|
|
then begin
|
|
// postpone description generation since we know we are transparent here
|
|
Result := True
|
|
end
|
|
else begin
|
|
Desc := GetRawImageDescriptionPtr;
|
|
Result := (Desc <> nil)
|
|
and (Desc^.Format <> ricfNone)
|
|
and (Desc^.AlphaPrec > 0);
|
|
//TODO: check for transparency through palette etc.
|
|
end;
|
|
end;
|
|
|
|
function TRasterImage.GetWidth: Integer;
|
|
var
|
|
Desc: PRawImageDescription;
|
|
begin
|
|
Desc := GetRawImageDescriptionPtr;
|
|
if (Desc = nil) or (Desc^.Format = ricfNone)
|
|
then Result := 0
|
|
else Result := Desc^.Width;
|
|
end;
|
|
|
|
function TRasterImage.HandleAllocated: boolean;
|
|
begin
|
|
Result := FSharedImage.FHandle <> 0;
|
|
end;
|
|
|
|
function TRasterImage.GetMimeType: string;
|
|
begin
|
|
{$IFDEF VerboseLCLTodos}{$note: implement}{$ENDIF}
|
|
|
|
(*
|
|
if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TCustomIcon) then begin
|
|
if FSharedImage.SaveStream<>nil then begin
|
|
case FSharedImage.SaveStreamType of
|
|
bnXPixmap: Result:=PredefinedClipboardMimeTypes[pcfPixmap];
|
|
else
|
|
Result:=PredefinedClipboardMimeTypes[pcfBitmap];
|
|
end;
|
|
end else
|
|
Result:=PredefinedClipboardMimeTypes[pcfBitmap];
|
|
end else
|
|
*)
|
|
|
|
Result:=inherited GetMimeType;
|
|
end;
|
|
|
|
procedure TRasterImage.LoadFromIntfImage(IntfImage: TLazIntfImage);
|
|
var
|
|
ImgHandle, ImgMaskHandle: HBitmap;
|
|
begin
|
|
IntfImage.CreateBitmaps(ImgHandle, ImgMaskHandle, not IntfImage.HasMask);
|
|
SetHandles(ImgHandle, ImgMaskHandle);
|
|
end;
|
|
|
|
procedure TRasterImage.FreeSaveStream;
|
|
begin
|
|
if FSharedImage.FSaveStream = nil then exit;
|
|
//DebugLn(['TRasterImage.FreeSaveStream A ',ClassName,' ',FImage.FSaveStream.Size]);
|
|
UnshareImage(false);
|
|
FreeAndNil(FSharedImage.FSaveStream);
|
|
end;
|
|
|
|
procedure TRasterImage.LoadFromMimeStream(AStream: TStream; const AMimeType: string);
|
|
begin
|
|
{$IFDEF VerboseLCLTodos}{$note Make oo}{$ENDIF}
|
|
if (ClassType=TBitmap) or (ClassType=TPixmap) or (ClassType=TCustomIcon) then begin
|
|
if (AnsiCompareText(AMimeType,PredefinedClipboardMimeTypes[pcfBitmap])=0)
|
|
or (AnsiCompareText(AMimeType,PredefinedClipboardMimeTypes[pcfPixmap])=0) then
|
|
begin
|
|
LoadFromStream(AStream);
|
|
exit;
|
|
end;
|
|
end;
|
|
inherited LoadFromMimeStream(AStream, AMimeType);
|
|
end;
|
|
|
|
procedure TRasterImage.LoadFromRawImage(const AImage: TRawImage; ADataOwner: Boolean);
|
|
var
|
|
img: PRawImage;
|
|
begin
|
|
BeginUpdate;
|
|
try
|
|
Clear;
|
|
if AImage.Description.Format = ricfNone then Exit; // empty image
|
|
|
|
img := GetRawImagePtr;
|
|
|
|
// Make sure, we do not free AImage.Data or Mask
|
|
if img^.Data = AImage.Data then begin
|
|
img^.Data := nil;
|
|
img^.DataSize := 0;
|
|
end;
|
|
if img^.Mask = AImage.Mask then begin
|
|
img^.Mask := nil;
|
|
img^.MaskSize := 0;
|
|
end;
|
|
if img^.Palette = AImage.Palette then begin
|
|
img^.Palette := nil;
|
|
img^.PaletteSize := 0;
|
|
end;
|
|
img^.FreeData;
|
|
|
|
img^.Description := AImage.Description;
|
|
if ADataOwner
|
|
then begin
|
|
img^.DataSize := AImage.DataSize;
|
|
img^.Data := AImage.Data;
|
|
img^.MaskSize := AImage.MaskSize;
|
|
img^.Mask := AImage.Mask;
|
|
img^.PaletteSize := AImage.PaletteSize;
|
|
img^.Palette := AImage.Palette;
|
|
end
|
|
else begin
|
|
// copy needed
|
|
img^.DataSize := AImage.DataSize;
|
|
if img^.DataSize > 0
|
|
then begin
|
|
GetMem(img^.Data, img^.DataSize);
|
|
Move(AImage.Data^, img^.Data^, img^.DataSize);
|
|
end
|
|
else img^.Data := nil;
|
|
|
|
img^.MaskSize := AImage.MaskSize;
|
|
if img^.MaskSize > 0
|
|
then begin
|
|
GetMem(img^.Mask, img^.MaskSize);
|
|
Move(AImage.Mask^, img^.Mask^, img^.MaskSize);
|
|
end
|
|
else img^.Mask := nil;
|
|
|
|
img^.PaletteSize := AImage.PaletteSize;
|
|
if img^.PaletteSize > 0
|
|
then begin
|
|
GetMem(img^.Palette, img^.PaletteSize);
|
|
Move(AImage.Palette^, img^.Palette^, img^.PaletteSize);
|
|
end
|
|
else img^.Palette := nil;
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
procedure TRasterImage.SaveToStream(AStream: TStream);
|
|
procedure Error;
|
|
begin
|
|
raise FPImageException.Create(rsErrorWhileSavingBitmap);
|
|
end;
|
|
var
|
|
Size, BytesWritten: Int64;
|
|
begin
|
|
SaveStreamNeeded;
|
|
if FSharedImage.SaveStream = nil
|
|
then Error;
|
|
|
|
FSharedImage.SaveStream.Position := 0;
|
|
Size := FSharedImage.SaveStream.Size;
|
|
if AStream is TMemoryStream
|
|
then TMemoryStream(AStream).SetSize(AStream.Position + Size);
|
|
|
|
BytesWritten := AStream.CopyFrom(FSharedImage.SaveStream, Size);
|
|
if BytesWritten <> Size
|
|
then Error;
|
|
end;
|
|
|
|
procedure TRasterImage.SetBitmapHandle(AValue: HBITMAP);
|
|
begin
|
|
if MaskHandleAllocated
|
|
then SetHandles(AValue, MaskHandle)
|
|
else SetHandles(AValue, 0);
|
|
end;
|
|
|
|
procedure TRasterImage.SetHandle(AValue: TLCLHandle);
|
|
begin
|
|
if FSharedImage.FHandle = AValue then Exit;
|
|
FreeCanvasContext;
|
|
UnshareImage(False);
|
|
FSharedImage.FHandle := AValue;
|
|
if FUpdateCount = 0
|
|
then Changed(Self);
|
|
end;
|
|
|
|
procedure TRasterImage.SetMaskHandle(AValue: HBITMAP);
|
|
begin
|
|
if BitmapHandleAllocated
|
|
then SetHandles(BitmapHandle, AValue)
|
|
else SetHandles(0, AValue);
|
|
end;
|
|
|
|
procedure TRasterImage.SetMasked(AValue: Boolean);
|
|
begin
|
|
if AValue = Masked then Exit;
|
|
|
|
FMasked := AValue;
|
|
Changed(Self);
|
|
end;
|
|
|
|
procedure TRasterImage.SetTransparentMode(AValue: TTransparentMode);
|
|
begin
|
|
if AValue = TransparentMode then exit;
|
|
FTransparentMode := AValue;
|
|
|
|
if AValue = tmAuto
|
|
then TransparentColor := clDefault
|
|
else TransparentColor := RequestTransparentColor;
|
|
end;
|
|
|
|
procedure TRasterImage.SetTransparent(AValue: Boolean);
|
|
var
|
|
lTransparent: Boolean;
|
|
begin
|
|
lTransparent := GetTransparent();
|
|
if AValue = lTransparent then Exit;
|
|
|
|
// some delphi compatibility, we can only change transparency through the mask.
|
|
Masked := AValue;
|
|
end;
|
|
|
|
// release handles without freeing them
|
|
// useful for creating a HBitmap
|
|
function TRasterImage.ReleaseBitmapHandle: HBITMAP;
|
|
begin
|
|
BitmapHandleNeeded;
|
|
FreeCanvasContext;
|
|
Result := InternalReleaseBitmapHandle;
|
|
end;
|
|
|
|
function TRasterImage.ReleaseMaskHandle: HBITMAP;
|
|
begin
|
|
MaskHandleNeeded;
|
|
FreeCanvasContext;
|
|
Result := InternalReleaseMaskHandle;
|
|
end;
|
|
|
|
function TRasterImage.ReleasePalette: HPALETTE;
|
|
begin
|
|
PaletteNeeded;
|
|
FreeCanvasContext;
|
|
Result := InternalReleasePalette;
|
|
end;
|
|
|
|
procedure TRasterImage.SaveStreamNeeded;
|
|
var
|
|
WorkStream: TMemoryStream;
|
|
begin
|
|
if FUpdateCount > 0
|
|
then raise EInvalidGraphicOperation.Create(rsRasterImageSaveInUpdate);
|
|
|
|
if FSharedImage.SaveStream <> nil then Exit;
|
|
|
|
WorkStream := TMemoryStream.Create;
|
|
try
|
|
WriteStream(WorkStream);
|
|
// Store our workstream as savestream
|
|
FSharedImage.SaveStream := WorkStream;
|
|
WorkStream := nil;
|
|
finally
|
|
// if something went wrong, free the workstream
|
|
WorkStream.Free;
|
|
end;
|
|
end;
|
|
|
|
function TRasterImage.CreateIntfImage: TLazIntfImage;
|
|
begin
|
|
{$IFDEF VerboseLCLTodos}{$note todo: create based on rawimage}{$ENDIF}
|
|
Result := TLazIntfImage.Create(0,0,[]);
|
|
Result.LoadFromBitmap(BitmapHandle, MaskHandle);
|
|
end;
|
|
|
|
procedure TRasterImage.CreateMask(AColor: TColor);
|
|
var
|
|
IntfImage: TLazIntfImage;
|
|
ImgHandle, MskHandle: HBitmap;
|
|
TransColor: TFPColor;
|
|
begin
|
|
//DebugLn(['TRasterImage.CreateMask ',Width,'x',Height,' ',Transparent,' ',dbgs(ord(TransparentMode)),' ',dbgs(TransparentColor)]);
|
|
if (Width = 0)
|
|
or (Height = 0)
|
|
or (AColor = clNone)
|
|
or ( (FTransparentMode = tmFixed)
|
|
and (FTransparentColor = clNone)
|
|
and (AColor = clDefault)
|
|
)
|
|
then begin
|
|
MaskHandle := 0;
|
|
Exit;
|
|
end;
|
|
|
|
{$IFDEF VerboseLCLTodos}{$note todo: move to IntfImage}{$ENDIF}
|
|
IntfImage := TLazIntfImage.Create(0,0,[]);
|
|
try
|
|
// force handle creation here, since at next step we will check for mask handle
|
|
ImgHandle := BitmapHandle;
|
|
// load from bitmap needs a mask handle otherwise no mask description is
|
|
// created.
|
|
if MaskHandleAllocated
|
|
then MskHandle := MaskHandle
|
|
else MskHandle := CreateBitmap(Width, Height, 1, 1, nil);
|
|
IntfImage.LoadFromBitmap(ImgHandle, MskHandle);
|
|
if not MaskHandleAllocated
|
|
then DeleteObject(MskHandle);
|
|
ImgHandle := 0;
|
|
|
|
if AColor = clDefault
|
|
then begin
|
|
if (FTransparentMode = tmFixed) and (FTransparentColor <> clDefault)
|
|
then TransColor := TColorToFPColor(ColorToRGB(FTransparentColor))
|
|
else TransColor := IntfImage.Colors[0, IntfImage.Height - 1];
|
|
end
|
|
else TransColor := TColorToFPColor(ColorToRGB(AColor));
|
|
|
|
IntfImage.Mask(TransColor);
|
|
|
|
IntfImage.CreateBitmaps(ImgHandle, MskHandle);
|
|
MaskHandle := MskHandle;
|
|
DeleteObject(ImgHandle);
|
|
finally
|
|
IntfImage.Free;
|
|
end;
|
|
end;
|
|
|
|
function TRasterImage.GetEmpty: boolean;
|
|
begin
|
|
Result := FSharedImage.IsEmpty;
|
|
end;
|
|
|
|
function TRasterImage.GetHandle: TLCLHandle;
|
|
begin
|
|
HandleNeeded;
|
|
Result := FSharedImage.FHandle;
|
|
end;
|
|
|
|
function TRasterImage.GetHeight: Integer;
|
|
var
|
|
Desc: PRawImageDescription;
|
|
begin
|
|
Desc := GetRawImageDescriptionPtr;
|
|
if (Desc = nil) or (Desc^.Format = ricfNone)
|
|
then Result := 0
|
|
else Result := Desc^.Height;
|
|
end;
|
|
|
|
function TRasterImage.GetMasked: Boolean;
|
|
begin
|
|
Result := FMasked;
|
|
end;
|
|
|
|
class function TRasterImage.GetSharedImageClass: TSharedRasterImageClass;
|
|
begin
|
|
Result := TSharedRasterImage;
|
|
end;
|
|
|
|
procedure TRasterImage.GetSize(out AWidth, AHeight: Integer);
|
|
var
|
|
Desc: PRawImageDescription;
|
|
begin
|
|
Desc := GetRawImageDescriptionPtr;
|
|
if (Desc = nil) or (Desc^.Format = ricfNone)
|
|
then begin
|
|
AWidth := 0;
|
|
AHeight := 0;
|
|
end
|
|
else begin
|
|
AWidth := Desc^.Width;
|
|
AHeight := Desc^.Height;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
procedure TRasterImage.ReadData(Stream: TStream);
|
|
function GetImageClass: TRasterImageClass;
|
|
const
|
|
// need to repeat here since they aren't defined yet
|
|
IconSignature: array [0..3] of char = #0#0#1#0;
|
|
CursorSignature: array [0..3] of char = #0#0#2#0;
|
|
var
|
|
Sig: array[0..7] of Char;
|
|
Position: Int64;
|
|
begin
|
|
Position := Stream.Position;
|
|
Stream.Read(Sig[0], SizeOf(Sig));
|
|
Stream.Position := Position;
|
|
|
|
if (Sig[0] = 'B') and (Sig[1] = 'M') then Exit(TBitmap);
|
|
if CompareMem(@Sig[0], @PNGcomn.Signature[0], 8) then Exit(TPortableNetworkGraphic);
|
|
if CompareMem(@Sig[0], @IconSignature[0], 4) then Exit(TIcon);
|
|
if CompareMem(@Sig[0], @CursorSignature[0], 4) then Exit(TCursorImage);
|
|
if TestStreamIsXPM(Stream) then Exit(TPixmap);
|
|
|
|
Result := nil;
|
|
end;
|
|
|
|
var
|
|
Size: Longint;
|
|
ImageClass: TRasterImageClass;
|
|
Image: TRasterImage;
|
|
begin
|
|
Stream.Read(Size, SizeOf(Size));
|
|
Size := LEtoN(Size);
|
|
|
|
// pre laz 0.9.26 there was no strict relation between graphic format and
|
|
// classtype, so we need to check if we need some conversion
|
|
if Size >= 8
|
|
then ImageClass := GetImageClass
|
|
else ImageClass := nil;
|
|
|
|
if (ImageClass = nil) or ClassType.InheritsFrom(ImageClass)
|
|
then begin
|
|
// no conversion needed, or it wasn't a known "old" format
|
|
LoadFromStream(Stream, Size);
|
|
Exit;
|
|
end;
|
|
|
|
Image := ImageClass.Create;
|
|
Image.LoadFromStream(Stream, Size);
|
|
try
|
|
Assign(Image);
|
|
finally
|
|
Image.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TRasterImage.WriteData(Stream: TStream);
|
|
procedure Error;
|
|
begin
|
|
raise FPImageException.Create(rsErrorWhileSavingBitmap);
|
|
end;
|
|
var
|
|
Size: Longint;
|
|
begin
|
|
SaveStreamNeeded;
|
|
if FSharedImage.SaveStream = nil
|
|
then Error;
|
|
|
|
Size := NtoLE(Longint(FSharedImage.SaveStream.Size));
|
|
Stream.Write(Size, SizeOf(Size));
|
|
SaveToStream(Stream);
|
|
end;
|
|
|
|
function TRasterImage.RequestTransparentColor: TColor;
|
|
var
|
|
RawImagePtr: PRawImage;
|
|
IntfImage: TLazIntfImage;
|
|
begin
|
|
// if RawImage exits then use it to get pixel overwise get it from the canvas
|
|
if Empty then
|
|
begin
|
|
Result := clNone;
|
|
Exit;
|
|
end;
|
|
|
|
RawImagePtr := GetRawImagePtr;
|
|
if RawImagePtr <> nil then
|
|
begin
|
|
IntfImage := TLazIntfImage.Create(RawImagePtr^, False);
|
|
try
|
|
Result := FPColorToTColor(IntfImage.Colors[0, Height - 1]);
|
|
finally
|
|
IntfImage.Free;
|
|
end;
|
|
end
|
|
else
|
|
Result := Canvas.GetPixel(0, Height - 1);
|
|
end;
|
|
|
|
procedure TRasterImage.SetWidth(AWidth: Integer);
|
|
begin
|
|
SetSize(AWidth, Height);
|
|
end;
|
|
|
|
procedure TRasterImage.SetHeight(AHeight: Integer);
|
|
begin
|
|
SetSize(Width, AHeight);
|
|
end;
|
|
|
|
// included by graphics.pp
|
|
|
|
|