| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- // SPDX-License-Identifier: LGPL-3.0-linking-exception
- {
- BCRoundedImage
- by Lainz
- Last modified: 2020-09-06 19:16 GMT-3
- Changelog:
- - 2020-09-06: Initial version supporting circle, rounded rectangle and square.
- Changing the quality of the resample, setting the rounding.
- OnPaintEvent to customize the final drawing.
- - 2025-01: MaxM, Changed class ancestor to TCustomBGRAGraphicControl;
- Added TBGRABitmap Bitmap draw;
- Added Stretch, Proportional, Alignments.
- }
- unit BCRoundedImage;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs,
- BGRABitmap, BGRABitmapTypes, BGRAGraphicControl, BCTypes;
- type
- TBCRoundedImage = class;
- // Event to draw before the image is sent to canvas
- //TBCRoundedImagePaintEvent = procedure (const Sender: TBCRoundedImage; const Bitmap: TBGRABitmap) of object;
- TBCRoundedImagePaintEvent = TBGRARedrawEvent;
- // Supported styles are circle, rounded rectangle and square
- TBCRoundedImageStyle = (isCircle, isRoundedRectangle, isSquare);
- // Control that draws an image within a rounded border
- { TBCRoundedImage }
- TBCRoundedImage = class(TCustomBGRAGraphicControl)
- private
- FBorderStyle: TRoundRectangleOptions;
- FOnPaintEvent: TBCRoundedImagePaintEvent;
- FPicture: TPicture;
- FImageBitmap: TBGRABitmap;
- FQuality: TResampleFilter;
- FStyle: TBCRoundedImageStyle;
- FRounding: single;
- FProportional: Boolean;
- FOnChange: TNotifyEvent;
- FAlignment: TAlignment;
- FStretch: Boolean;
- FVerticalAlignment: TTextLayout;
- function GetOnPaintEvent: TBCRoundedImagePaintEvent;
- procedure SetAlignment(AValue: TAlignment);
- procedure SetBitmap(AValue: TBGRABitmap);
- procedure SetBorderStyle(AValue: TRoundRectangleOptions);
- procedure SetOnPaintEvent(AValue: TBCRoundedImagePaintEvent);
- procedure SetPicture(AValue: TPicture);
- procedure SetProportional(AValue: Boolean);
- procedure SetQuality(AValue: TResampleFilter);
- procedure SetStretch(AValue: Boolean);
- procedure SetStyle(AValue: TBCRoundedImageStyle);
- procedure SetRounding(AValue: single);
- procedure SetVerticalAlignment(AValue: TTextLayout);
- protected
- procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer; WithThemeSpace: boolean); override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure Paint; override;
- procedure Draw(ABitmap: TBGRABitmap);
- property Bitmap: TBGRABitmap read FImageBitmap write setBitmap;
- published
- // The image that's used as background
- property Picture: TPicture read FPicture write SetPicture;
- // The style can be circle, rounded rectangle or square
- property Style: TBCRoundedImageStyle read FStyle write SetStyle;
- // The style of the rounded rectangle
- property BorderStyle: TRoundRectangleOptions read FBorderStyle write SetBorderStyle;
- // Rounding is used when you choose the rounded rectangle style
- property Rounding: single read FRounding write SetRounding;
- // The quality when resizing the image
- property Quality: TResampleFilter read FQuality write SetQuality;
- // Stretch Proportianally
- property Proportional: Boolean read FProportional write SetProportional;
- property Stretch: Boolean read FStretch write SetStretch default True;
- // Alignments of the Image inside the Control
- property Alignment: TAlignment read FAlignment write SetAlignment default taCenter;
- property VerticalAlignment: TTextLayout read FVerticalAlignment write SetVerticalAlignment default tlCenter;
- property OnChange: TNotifyEvent read FOnChange write FOnChange;
- // You can paint before the bitmap is drawn on canvas
- property OnPaintEvent: TBCRoundedImagePaintEvent read GetOnPaintEvent write SetOnPaintEvent; deprecated 'Use OnRedraw instead';
- property Anchors;
- property Align;
- property OnMouseEnter;
- property OnMouseLeave;
- property OnClick;
- end;
- { #todo -oMaxM : we could move it to a common unit and use it in BGRAImageList too }
- function CalcProportionalRect(AWidth, AHeight, AImageWidth, AImageHeight: Integer;
- AHorizAlign: TAlignment; AVertAlign: TTextLayout): TRect;
- procedure Register;
- implementation
- function CalcProportionalRect(AWidth, AHeight, AImageWidth, AImageHeight: Integer; AHorizAlign: TAlignment;
- AVertAlign: TTextLayout): TRect;
- var
- rW, rH:Single;
- newWidth,
- newHeight:Integer;
- begin
- FillChar(Result, sizeof(Result), 0);
- if (AImageWidth > 0) and (AImageHeight > 0) then
- begin
- rW := AImageWidth / AWidth;
- rH := AImageHeight / AHeight;
- if (rW > rH)
- then begin
- newHeight:= round(AImageHeight / rW);
- newWidth := AWidth;
- end
- else begin
- newWidth := round(AImageWidth / rH);
- newHeight := AHeight;
- end;
- case AHorizAlign of
- taCenter: Result.Left:= (AWidth-newWidth) div 2;
- taRightJustify: Result.Left:= AWidth-newWidth;
- end;
- case AVertAlign of
- tlCenter: Result.Top:= (AHeight-newHeight) div 2;
- tlBottom: Result.Top:= AHeight-newHeight;
- end;
- Result.Right:= Result.Left+newWidth;
- Result.Bottom:= Result.Top+newHeight;
- end;
- end;
- procedure Register;
- begin
- RegisterComponents('BGRA Controls', [TBCRoundedImage]);
- end;
- procedure TBCRoundedImage.SetProportional(AValue: Boolean);
- begin
- if FProportional=AValue then Exit;
- FProportional:=AValue;
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- procedure TBCRoundedImage.SetBorderStyle(AValue: TRoundRectangleOptions);
- begin
- if FBorderStyle=AValue then Exit;
- FBorderStyle:=AValue;
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- function TBCRoundedImage.GetOnPaintEvent: TBCRoundedImagePaintEvent;
- begin
- Result:= OnRedraw;
- end;
- procedure TBCRoundedImage.SetAlignment(AValue: TAlignment);
- begin
- if FAlignment=AValue then Exit;
- FAlignment:=AValue;
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- procedure TBCRoundedImage.SetBitmap(AValue: TBGRABitmap);
- begin
- if (AValue <> FImageBitmap) then
- begin
- // Clear actual image
- FImageBitmap.Free;
- FImageBitmap :=TBGRABitmap.Create(Width, Height, BGRAPixelTransparent);
- if (AValue<>nil) then FImageBitmap.Assign(AValue, True); // Associate the new bitmap
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- end;
- procedure TBCRoundedImage.SetPicture(AValue: TPicture);
- begin
- if (AValue <> FPicture) then
- begin
- // Clear actual Picture
- FPicture.Free;
- FPicture :=TPicture.Create;
- if (AValue<>nil) then FPicture.Assign(AValue); // Associate the new Picture
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- end;
- procedure TBCRoundedImage.SetOnPaintEvent(AValue: TBCRoundedImagePaintEvent);
- begin
- OnRedraw:= AValue;
- end;
- procedure TBCRoundedImage.SetQuality(AValue: TResampleFilter);
- begin
- if FQuality = AValue then
- Exit;
- FQuality := AValue;
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- procedure TBCRoundedImage.SetStretch(AValue: Boolean);
- begin
- if FStretch=AValue then Exit;
- FStretch:=AValue;
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- procedure TBCRoundedImage.SetStyle(AValue: TBCRoundedImageStyle);
- begin
- if FStyle = AValue then
- Exit;
- FStyle := AValue;
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- procedure TBCRoundedImage.SetRounding(AValue: single);
- begin
- if FRounding = AValue then
- Exit;
- FRounding := AValue;
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- procedure TBCRoundedImage.SetVerticalAlignment(AValue: TTextLayout);
- begin
- if FVerticalAlignment=AValue then Exit;
- FVerticalAlignment:=AValue;
- if Assigned(FOnChange) then FOnChange(Self);
- Invalidate;
- end;
- {$hints off}
- procedure TBCRoundedImage.CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer; WithThemeSpace: boolean);
- begin
- PreferredWidth := 100;
- PreferredHeight := 100;
- end;
- constructor TBCRoundedImage.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FAlignment:= taCenter;
- FVerticalAlignment:= tlCenter;
- FStretch:= True;
- // Create the Image Bitmap
- FPicture := TPicture.Create;
- FImageBitmap := TBGRABitmap.Create;
- FRounding := 10;
- FQuality := rfBestQuality;
- FBGRA.FillTransparent;
- end;
- destructor TBCRoundedImage.Destroy;
- begin
- FPicture.Free;
- FImageBitmap.Free;
- inherited Destroy;
- end;
- procedure TBCRoundedImage.Paint;
- begin
- if (ClientWidth <> FBGRA.Width) or (ClientHeight <> FBGRA.Height)
- then FBGRA.SetSize(ClientWidth, ClientHeight);
- Draw(FBGRA);
- if Assigned(OnRedraw) then OnRedraw(Self, FBGRA);
- FBGRA.Draw(Canvas, 0, 0, False);
- end;
- procedure TBCRoundedImage.Draw(ABitmap: TBGRABitmap);
- var
- image,
- imageD: TBGRABitmap;
- imgRect: TRect;
- begin
- ABitmap.FillTransparent;
- if ((FPicture.Width = 0) or (FPicture.Height = 0)) and
- FImageBitmap.Empty then exit;
- try
- if FImageBitmap.Empty
- then image := TBGRABitmap.Create(FPicture.Bitmap)
- else image := TBGRABitmap.Create(FImageBitmap.Bitmap);
- imageD:= TBGRABitmap.Create(Width, Height, BGRAPixelTransparent);
- if FProportional
- then imgRect:= CalcProportionalRect(Width, Height, image.Width, image.Height,
- FAlignment, FVerticalAlignment)
- else begin
- if FStretch
- then imgRect:= Rect(0,0,Width,Height)
- else begin
- case FAlignment of
- taLeftJustify: imgRect.Left:= 0;
- taCenter: imgRect.Left:= (Width-image.Width) div 2;
- taRightJustify: imgRect.Left:= Width-image.Width;
- end;
- case FVerticalAlignment of
- tlTop: imgRect.Top:= 0;
- tlCenter: imgRect.Top:= (Height-image.Height) div 2;
- tlBottom: imgRect.Top:= Height-image.Height;
- end;
- imgRect.Right:= imgRect.Left+image.Width;
- imgRect.Bottom:= imgRect.Top+image.Height;
- end;
- end;
- if FStretch or FProportional then
- begin
- // Stretch with Quality
- image.ResampleFilter := FQuality;
- BGRAReplace(image, image.Resample(imgRect.Width, imgRect.Height));
- end;
- imageD.PutImage(imgRect.Left, imgRect.Top, image, dmDrawWithTransparency);
- // Style
- case FStyle of
- isCircle: ABitmap.FillEllipseAntialias(Width div 2, Height div 2,
- (Width div 2)-FRounding, (Height div 2)-FRounding, imageD);
- isRoundedRectangle: ABitmap.FillRoundRectAntialias(0, 0, Width,
- Height, FRounding, FRounding, imageD, FBorderStyle);
- else ABitmap.PutImage(0, 0, imageD, dmDrawWithTransparency);
- end;
- finally
- imageD.Free;
- image.Free;
- end;
- end;
- end.
|