lazarus/lcl/include/custombitmap.inc

511 lines
13 KiB
PHP

{%MainUnit ../graphics.pp}
{******************************************************************************
TCustomBitmap
******************************************************************************
*****************************************************************************
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.
*****************************************************************************
}
function TCustomBitmap.BitmapHandleAllocated: boolean;
begin
// for custombitmap handle = bitmaphandle
Result := FSharedImage.FHandle <> 0;
end;
function TCustomBitmap.CanShareImage(AClass: TSharedRasterImageClass): Boolean;
begin
Result := (AClass <> TSharedCustomBitmap)
and inherited CanShareImage(AClass);
end;
procedure TCustomBitmap.Changed(Sender: TObject);
begin
// When the bitmap is changed by the canvas, the rawimage data isn't valid anymore
if Sender = FCanvas
then TSharedCustomBitmap(FSharedImage).FImage.FreeData;
inherited Changed(Sender);
end;
procedure TCustomBitmap.Clear;
begin
FPixelFormat := pfDevice;
inherited Clear;
end;
procedure TCustomBitmap.FreeImage;
begin
inherited FreeImage;
TSharedCustomBitmap(FSharedImage).FreeImage;
end;
constructor TCustomBitmap.Create;
begin
inherited Create;
FPixelFormat := pfDevice;
end;
destructor TCustomBitmap.Destroy;
begin
FreeMaskHandle;
inherited Destroy;
end;
procedure TCustomBitmap.Assign(Source: TPersistent);
begin
inherited Assign(Source);
if Source is TCustomBitmap then
begin
FPixelFormat := TCustomBitmap(Source).FPixelFormat;
FPixelFormatNeedsUpdate := TCustomBitmap(Source).FPixelFormatNeedsUpdate;
end;
end;
function TCustomBitmap.CreateDefaultBitmapHandle(const ADesc: TRawImageDescription): HBITMAP;
var
DC: HDC;
BI: TBitmapInfo;
P: Pointer;
begin
if ADesc.Depth = 1
then begin
Result := CreateBitmap(ADesc.Width, ADesc.Height, 1, ADesc.Depth, nil);
//AType := bmDDB;
end
else begin
// on windows we need a DIB section
FillChar(BI.bmiHeader, SizeOf(BI.bmiHeader), 0);
BI.bmiHeader.biSize := SizeOf(BI.bmiHeader);
BI.bmiHeader.biWidth := ADesc.Width;
BI.bmiHeader.biHeight := -ADesc.Height; // request top down
BI.bmiHeader.biPlanes := 1;
BI.bmiHeader.biBitCount := ADesc.Depth;
BI.bmiHeader.biCompression := BI_RGB;
DC := GetDC(0);
p := nil;
Result := CreateDIBSection(DC, BI, DIB_RGB_COLORS, p, 0, 0);
//AType := bmDIB;
ReleaseDC(0, DC);
// fallback for other widgetsets not implementing CreateDIBSection
// we need the DIB section anyway someday if we want a scanline
if Result = 0
then begin
Result := CreateBitmap(ADesc.Width, ADesc.Height, 1, ADesc.Depth, nil);
//AType := bmDDB;
end;
end;
end;
procedure TCustomBitmap.FreeMaskHandle;
begin
if FMaskHandle = 0 then Exit;
DeleteObject(FMaskHandle);
FMaskHandle := 0;
end;
procedure TCustomBitmap.HandleNeeded;
begin
BitmapHandleNeeded;
end;
function TCustomBitmap.MaskHandleAllocated: boolean;
begin
Result := FMaskHandle <> 0;
end;
procedure TCustomBitmap.MaskHandleNeeded;
var
ImagePtr: PRawImage;
MaskImage: TRawImage;
msk, dummy: HBITMAP;
begin
if FMaskHandle <> 0 then Exit;
if not Masked then Exit;
if TransparentMode = tmAuto then
begin
BitmapHandleNeeded; // create together with bitmaphandle
if FMaskHandle <> 0 then Exit;
ImagePtr := GetRawImagePtr;
if ImagePtr^.Description.Format = ricfNone then Exit;
// check if we have mask data
if ImagePtr^.IsMasked(False)
then begin
// move mask to image data, so we only have to create one handle
// (and don't have to think about imagehandle format)
MaskImage.Init;
MaskImage.Description := ImagePtr^.Description.GetDescriptionFromMask;
MaskImage.DataSize := ImagePtr^.MaskSize;
MaskImage.Data := ImagePtr^.Mask;
if CreateCompatibleBitmaps(MaskImage, msk, dummy, True)
then begin
if BitmapHandleAllocated
then UpdateHandles(BitmapHandle, msk)
else UpdateHandles(0, msk);
Exit;
end;
end;
end;
// no data or transparent color is set - create ourselves
CreateMask;
end;
function TCustomBitmap.PaletteAllocated: boolean;
begin
Result := TSharedCustomBitmap(FSharedImage).FPalette <> 0;
end;
procedure TCustomBitmap.PaletteNeeded;
begin
// TODO: implement
end;
procedure TCustomBitmap.RawimageNeeded(ADescOnly: Boolean);
var
OldChangeEvent: TNotifyEvent;
ImagePtr: PRawImage;
Flags: TRawImageQueryFlags;
begin
ImagePtr := @TSharedCustomBitmap(FSharedImage).FImage;
if ImagePtr^.Description.Format <> ricfNone
then begin
// description valid
if ADescOnly then Exit;
if (ImagePtr^.Data <> nil) and (ImagePtr^.DataSize > 0) then Exit;
if ImagePtr^.Description.Width = 0 then Exit; // no data
if ImagePtr^.Description.Height = 0 then Exit; // no data
end;
// use savestream if present
if FSharedImage.FSaveStream <> nil
then begin
FSharedImage.FSaveStream.Position := 0;
OldChangeEvent := OnChange;
try
OnChange := nil;
ReadStream(FSharedImage.FSaveStream, FSharedImage.FSaveStream.Size);
FPixelFormatNeedsUpdate := True;
finally
OnChange := OldChangeEvent;
end;
end;
// use handle
if FSharedImage.FHandle <> 0
then begin
if ADescOnly
or not RawImage_FromBitmap(ImagePtr^, FSharedImage.FHandle, FMaskHandle)
then ImagePtr^.Description := GetDescriptionFromBitmap(FSharedImage.FHandle);
FPixelFormatNeedsUpdate := True;
end;
// setup ImagePtr, fill description if not set
if ImagePtr^.Description.Format = ricfNone
then begin
// use query to get a default description without alpha, since alpha drawing
// is not yet supported (unless asked for)
// use var and not pixelformat property since it requires a rawimagedescription (which we are creating)
case FPixelFormat of
pf1bit: Flags := [riqfMono, riqfMask];
pf4bit,
pf8bit: Flags := [riqfGrey, riqfMask, riqfPalette];
pf32bit: Flags := [riqfRGB, riqfMask, riqfAlpha];
else
Flags := [riqfRGB, riqfMask];
end;
ImagePtr^.Description := QueryDescription(Flags, ImagePtr^.Description.Width, ImagePtr^.Description.Height);
// atleast for now let pixelformat reflect the created description
FPixelFormatNeedsUpdate := True;
end;
if ADescOnly then Exit;
if ImagePtr^.Data <> nil then Exit;
if ImagePtr^.DataSize > 0 then Exit;
// setup data
ImagePtr^.CreateData(True);
end;
function TCustomBitmap.ReleaseHandle: HBITMAP;
begin
HandleNeeded;
Result := FSharedImage.ReleaseHandle;
end;
procedure TCustomBitmap.SetBitmapHandle(const AValue: HBITMAP);
begin
inherited SetBitmapHandle(AValue);
end;
function TCustomBitmap.LazarusResourceTypeValid(const ResourceType: string): boolean;
var
ResType: String;
begin
if Length(ResourceType) < 3 then Exit(False);
ResType := UpperCase(ResourceType);
case ResType[1] of
'B': begin
Result := (ResType = 'BMP') or (ResType = 'BITMAP');
end;
'X': begin
Result := Restype = 'XPM';
end;
else
Result := False;
end;
end;
function TCustomBitmap.GetHandleType: TBitmapHandleType;
begin
Result := TSharedCustomBitmap(FSharedImage).HandleType;
end;
function TCustomBitmap.GetMaskHandle: HBITMAP;
begin
MaskHandleNeeded;
Result := FMaskHandle;
end;
procedure TCustomBitmap.SetHandleType(AValue: TBitmapHandleType);
begin
if HandleType = AValue then exit;
{$IFNDEF DisableChecks}
DebugLn('TCustomBitmap.SetHandleType TCustomBitmap.SetHandleType not implemented');
{$ENDIF}
end;
procedure TCustomBitmap.SetMonochrome(AValue: Boolean);
begin
if Monochrome = AValue then exit;
if AValue
then PixelFormat := pf1bit
else PixelFormat := pfDevice;
end;
procedure TCustomBitmap.SetPixelFormat(AValue: TPixelFormat);
begin
if AValue = FPixelFormat then Exit;
{$IFDEF VerboseLCLTodos}{$note todo copy image into new format }{$ENDIF}
FreeImage;
FPixelFormat := AValue;
end;
procedure TCustomBitmap.SetSize(AWidth, AHeight: integer);
var
SCB: TSharedCustomBitmap;
CurIntfImage, NewIntfImage: TLazIntfImage;
NewRawImage: TRawImage;
begin
RawImageNeeded(True);
if AWidth < 0 then AWidth := 0;
if AHeight < 0 then AHeight := 0;
SCB := TSharedCustomBitmap(FSharedImage);
if (SCB.FImage.Description.Height = cardinal(AHeight))
and (SCB.FImage.Description.Width = cardinal(AWidth))
then Exit;
UnshareImage(False);
// FSHaredImage might have been changed by UnshareImage
SCB := TSharedCustomBitmap(FSharedImage);
// for delphi compatibility copy old image
RawImageNeeded(False);
if (SCB.FImage.Description.Height >= cardinal(AHeight))
and (SCB.FImage.Description.Width >= cardinal(AWidth))
then begin
// use the faster ExtractRect. Since it calculates the intersection of source
// and requested rect we can only use it when shrinking the image.
SCB.FImage.ExtractRect(Rect(0, 0, AWidth, AHeight), NewRawImage);
end
else begin
// use slow copy of pixeldata till rawimage can also copy to larger destination
NewRawImage.Description := SCB.FImage.Description;
NewRawImage.Description.Width := AWidth;
NewRawImage.Description.Height := AHeight;
NewRawImage.ReleaseData;
if SCB.FImage.DataSize > 0 then
begin
NewRawImage.CreateData(True);
CurIntfImage := TLazIntfImage.Create(SCB.FImage, False);
NewIntfImage := TLazIntfImage.Create(NewRawImage, False);
NewIntfImage.CopyPixels(CurIntfImage);
CurIntfImage.Free;
NewIntfImage.Free;
end;
end;
SCB.FImage.FreeData;
SCB.FImage := NewRawImage;
// size was changed => update HDC and HBITMAP
FreeCanvasContext;
SCB.FreeHandle;
FreeMaskHandle;
Changed(Self);
end;
procedure TCustomBitmap.UpdatePixelFormat;
begin
RawimageNeeded(True);
FPixelFormat := TSharedCustomBitmap(FSharedImage).GetPixelFormat;
FPixelFormatNeedsUpdate := False;
end;
function TCustomBitmap.GetMonochrome: Boolean;
begin
RawImageNeeded(False);
Result := TSharedCustomBitmap(FSharedImage).FImage.Description.Depth = 1;
end;
procedure TCustomBitmap.UnshareImage(CopyContent: boolean);
var
NewImage: TSharedCustomBitmap;
OldImage: TSharedCustomBitmap;
begin
if FSharedImage.RefCount <= 1 then Exit;
// release old FImage and create a new one
OldImage := FSharedImage as TSharedCustomBitmap;
NewImage := GetSharedImageClass.Create as TSharedCustomBitmap;
try
NewImage.Reference;
if CopyContent and OldImage.ImageAllocated
then begin
// force a complete rawimage, so we can copy it
RawimageNeeded(False);
OldImage.FImage.ExtractRect(Rect(0, 0, Width, Height), NewImage.FImage);
end
else begin
// keep width, height and bpp
NewImage.FImage.Description := OldImage.FImage.Description;
end;
FreeCanvasContext;
FSharedImage := NewImage;
NewImage := nil; // transaction sucessful
OldImage.Release;
finally
// in case something goes wrong, keep old and free new
NewImage.Free;
end;
end;
function TCustomBitmap.UpdateHandles(ABitmap, AMask: HBITMAP): Boolean;
begin
// Update sets the handles corresponding to our rawimage and/or savestream, so
// we do not free them here.
Result := False;
if FSharedImage.FHandle <> ABitmap
then begin
FSharedImage.FreeHandle;
// get the properties from new bitmap
FSharedImage.FHandle := ABitmap;
Result := True;
end;
if FMaskHandle <> AMask
then begin
FreeMaskHandle;
FMaskHandle := AMask;
Result := True;
end;
end;
function TCustomBitmap.GetBitmapHandle: HBITMAP;
begin
BitmapHandleNeeded;
Result := FSharedImage.FHandle;
end;
procedure TCustomBitmap.SetHandles(ABitmap, AMask: HBITMAP);
begin
if FSharedImage.FHandle <> ABitmap
then begin
// if the handle is set externally we should unshare ourselves
FreeCanvasContext;
UnshareImage(false);
FreeSaveStream;
TSharedCustomBitmap(FSharedImage).FreeImage;
end;
if UpdateHandles(ABitmap, AMask)
then begin
FPixelFormatNeedsUpdate := True;
FMasked := AMask <> 0;
Changed(Self);
end;
end;
procedure TCustomBitmap.SetHandle(AValue: THandle);
begin
// for TCustomBitmap BitmapHandle = Handle
BitmapHandle := AValue;
end;
function TCustomBitmap.InternalReleaseBitmapHandle: HBITMAP;
begin
Result := FSharedImage.ReleaseHandle;
end;
function TCustomBitmap.InternalReleaseMaskHandle: HBITMAP;
begin
Result := FMaskHandle;
FMaskHandle := 0;
end;
function TCustomBitmap.InternalReleasePalette: HPALETTE;
begin
Result := TSharedCustomBitmap(FSharedImage).ReleasePalette;
end;
function TCustomBitmap.GetPalette: HPALETTE;
begin
PaletteNeeded;
Result := TSharedCustomBitmap(FSharedImage).FPalette;
end;
function TCustomBitmap.GetPixelFormat: TPixelFormat;
begin
if FPixelFormatNeedsUpdate
then UpdatePixelFormat;
Result := FPixelFormat;
end;
function TCustomBitmap.GetRawImagePtr: PRawImage;
begin
RawimageNeeded(False);
Result := @TSharedCustomBitmap(FSharedImage).FImage;
end;
function TCustomBitmap.GetRawImageDescriptionPtr: PRawImageDescription;
begin
RawimageNeeded(True);
Result := @TSharedCustomBitmap(FSharedImage).FImage.Description;
end;
class function TCustomBitmap.GetSharedImageClass: TSharedRasterImageClass;
begin
Result := TSharedCustomBitmap;
end;