573 lines
15 KiB
ObjectPascal
573 lines
15 KiB
ObjectPascal
// SPDX-License-Identifier: LGPL-3.0-linking-exception
|
|
{
|
|
Created by BGRA Controls Team
|
|
Dibo, Circular, lainz (007) and contributors.
|
|
For detailed information see readme.txt
|
|
|
|
Site: https://sourceforge.net/p/bgra-controls/
|
|
Wiki: http://wiki.lazarus.freepascal.org/BGRAControls
|
|
Forum: http://forum.lazarus.freepascal.org/index.php/board,46.0.html
|
|
}
|
|
{******************************* CONTRIBUTOR(S) ******************************
|
|
- Edivando S. Santos Brasil | mailedivando@gmail.com
|
|
(Compatibility with delphi VCL 11/2018)
|
|
|
|
***************************** END CONTRIBUTOR(S) *****************************}
|
|
unit BGRAVirtualScreen;
|
|
|
|
{$I bgracontrols.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, {$IFDEF FPC}LMessages, LResources, LCLIntf,{$ENDIF} Types, Forms, BCBaseCtrls, Controls, Graphics, Dialogs,
|
|
{$IFNDEF FPC}Windows, Messages, BGRAGraphics, GraphType, FPImage, {$ENDIF}
|
|
ExtCtrls, BGRABitmap, BCTypes;
|
|
|
|
type
|
|
{ TCustomBGRAVirtualScreen }
|
|
|
|
TCustomBGRAVirtualScreen = class(TBGRACustomPanel)
|
|
private
|
|
{ Private declarations }
|
|
FBGRA: TBGRABitmap;
|
|
FOnRedraw: TBGRARedrawEvent;
|
|
FDiscardedRect: TRect;
|
|
FBevelInner, FBevelOuter: TPanelBevel;
|
|
FBevelWidth: TBevelWidth;
|
|
FBorderWidth: TBorderWidth;
|
|
FAlignment: TAlignment;
|
|
FBitmapAutoScale: boolean;
|
|
function GetBitmapHeight: integer;
|
|
function GetBitmapScale: double;
|
|
function GetBitmapWidth: integer;
|
|
function GetVSCaption: string;
|
|
procedure SetAlignment(const Value: TAlignment);
|
|
procedure SetBevelInner(const AValue: TPanelBevel);
|
|
procedure SetBevelOuter(const AValue: TPanelBevel);
|
|
procedure SetBevelWidth(const AValue: TBevelWidth);
|
|
procedure SetBitmapAutoScale(AValue: boolean);
|
|
procedure SetBorderWidth(const AValue: TBorderWidth);
|
|
procedure SetVSCaption(AValue: string);
|
|
protected
|
|
{ Protected declarations }
|
|
procedure Paint; override;
|
|
procedure Resize; override;
|
|
procedure BGRASetSize(AWidth, AHeight: integer);
|
|
procedure RedrawBitmapContent; virtual;
|
|
procedure SetColor(Value: TColor); {$IFDEF FPC}override;{$ENDIF}
|
|
procedure WMEraseBkgnd(var Message: {$IFDEF FPC}TLMEraseBkgnd{$ELSE}TWMEraseBkgnd{$ENDIF}); message {$IFDEF FPC}LM_ERASEBKGND{$ELSE}WM_ERASEBKGND{$ENDIF};
|
|
procedure SetEnabled(Value: boolean); override;
|
|
public
|
|
{ Public declarations }
|
|
constructor Create(TheOwner: TComponent); override;
|
|
function BitmapRectToClient(ARect: TRect): TRect;
|
|
procedure RedrawBitmap; overload;
|
|
procedure RedrawBitmap(ARect: TRect); overload;
|
|
procedure RedrawBitmap(ARectArray: array of TRect); overload;
|
|
procedure DiscardBitmap; overload;
|
|
procedure DiscardBitmap(ARect: TRect); overload;
|
|
procedure InvalidateBitmap(ARect: TRect);
|
|
destructor Destroy; override;
|
|
public
|
|
property OnRedraw: TBGRARedrawEvent Read FOnRedraw Write FOnRedraw;
|
|
property Bitmap: TBGRABitmap Read FBGRA;
|
|
property BitmapAutoScale: boolean read FBitmapAutoScale write SetBitmapAutoScale default true;
|
|
property BitmapScale: double read GetBitmapScale;
|
|
property BitmapWidth: integer read GetBitmapWidth;
|
|
property BitmapHeight: integer read GetBitmapHeight;
|
|
property BorderWidth: TBorderWidth Read FBorderWidth Write SetBorderWidth default 0;
|
|
property BevelInner: TPanelBevel Read FBevelInner Write SetBevelInner default bvNone;
|
|
property BevelOuter: TPanelBevel Read FBevelOuter Write SetBevelOuter default bvNone;
|
|
property BevelWidth: TBevelWidth Read FBevelWidth Write SetBevelWidth default 1;
|
|
property Alignment: TAlignment Read FAlignment Write SetAlignment;
|
|
property Caption: string read GetVSCaption write SetVSCaption;
|
|
end;
|
|
|
|
TBGRAVirtualScreen = class(TCustomBGRAVirtualScreen)
|
|
published
|
|
property OnRedraw;
|
|
property Bitmap;
|
|
property BitmapAutoScale;
|
|
// TPanel
|
|
property Align;
|
|
property Alignment;
|
|
property Anchors;
|
|
property AutoSize;
|
|
property BorderSpacing;
|
|
property ChildSizing;
|
|
{$IFDEF FPC} //#
|
|
property OnGetDockCaption;
|
|
{$ENDIF}
|
|
property BevelInner;
|
|
property BevelOuter;
|
|
property BevelWidth;
|
|
property BidiMode;
|
|
property BorderWidth;
|
|
property BorderStyle;
|
|
property Caption;
|
|
property ClientHeight;
|
|
property ClientWidth;
|
|
property Color;
|
|
property Constraints;
|
|
property DockSite;
|
|
property DragCursor;
|
|
property DragKind;
|
|
property DragMode;
|
|
property Enabled;
|
|
property Font;
|
|
property FullRepaint;
|
|
property ParentBidiMode;
|
|
property ParentColor;
|
|
property ParentFont;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property ShowHint;
|
|
property TabOrder;
|
|
property TabStop;
|
|
property UseDockManager default True;
|
|
property Visible;
|
|
property OnClick;
|
|
property OnContextPopup;
|
|
property OnDockDrop;
|
|
property OnDockOver;
|
|
property OnDblClick;
|
|
property OnDragDrop;
|
|
property OnDragOver;
|
|
property OnEndDock;
|
|
property OnEndDrag;
|
|
property OnEnter;
|
|
property OnExit;
|
|
property OnGetSiteInfo;
|
|
property OnMouseDown;
|
|
property OnMouseEnter;
|
|
property OnMouseLeave;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnMouseWheel;
|
|
property OnMouseWheelDown;
|
|
property OnMouseWheelUp;
|
|
property OnResize;
|
|
property OnStartDock;
|
|
property OnStartDrag;
|
|
property OnUnDock;
|
|
end;
|
|
|
|
{$IFDEF FPC}procedure Register;{$ENDIF}
|
|
|
|
implementation
|
|
|
|
uses BGRABitmapTypes, math, LazVersion;
|
|
|
|
{$IFDEF FPC}
|
|
procedure Register;
|
|
begin
|
|
RegisterComponents('BGRA Controls', [TBGRAVirtualScreen]);
|
|
end;
|
|
{$ENDIF}
|
|
{ TCustomBGRAVirtualScreen }
|
|
|
|
procedure TCustomBGRAVirtualScreen.SetAlignment(const Value: TAlignment);
|
|
begin
|
|
if FAlignment = Value then
|
|
exit;
|
|
FAlignment := Value;
|
|
DiscardBitmap;
|
|
end;
|
|
|
|
function TCustomBGRAVirtualScreen.GetVSCaption: string;
|
|
begin
|
|
result := inherited Caption;
|
|
end;
|
|
|
|
function TCustomBGRAVirtualScreen.GetBitmapScale: double;
|
|
begin
|
|
{$if laz_fullversion >= 2000000}
|
|
if not FBitmapAutoScale then
|
|
result := GetCanvasScaleFactor
|
|
else
|
|
result := 1;
|
|
{$else}
|
|
result := 1;
|
|
{$endif}
|
|
end;
|
|
|
|
function TCustomBGRAVirtualScreen.GetBitmapHeight: integer;
|
|
begin
|
|
result := round(ClientHeight * BitmapScale);
|
|
end;
|
|
|
|
function TCustomBGRAVirtualScreen.GetBitmapWidth: integer;
|
|
begin
|
|
result := round(ClientWidth * BitmapScale);
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.SetBevelInner(const AValue: TPanelBevel);
|
|
begin
|
|
if FBevelInner = AValue then
|
|
exit;
|
|
FBevelInner := AValue;
|
|
DiscardBitmap;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.SetBevelOuter(const AValue: TPanelBevel);
|
|
begin
|
|
if FBevelOuter = AValue then
|
|
exit;
|
|
FBevelOuter := AValue;
|
|
DiscardBitmap;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.SetBevelWidth(const AValue: TBevelWidth);
|
|
begin
|
|
if FBevelWidth = AValue then
|
|
exit;
|
|
FBevelWidth := AValue;
|
|
DiscardBitmap;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.SetBitmapAutoScale(AValue: boolean);
|
|
begin
|
|
if FBitmapAutoScale=AValue then Exit;
|
|
DiscardBitmap; //before to get correct invalidate bounds
|
|
FBitmapAutoScale:=AValue;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.SetBorderWidth(const AValue: TBorderWidth);
|
|
begin
|
|
if FBorderWidth = AValue then
|
|
exit;
|
|
FBorderWidth := AValue;
|
|
DiscardBitmap;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.SetVSCaption(AValue: string);
|
|
begin
|
|
inherited Caption := AValue;
|
|
DiscardBitmap;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.Paint;
|
|
begin
|
|
{$IFDEF WINDOWS}
|
|
// to avoid flickering in Windows running without themes (classic style)
|
|
DoubleBuffered := ControlCount <> 0;
|
|
{$ENDIF}
|
|
BGRASetSize(BitmapWidth, BitmapHeight);
|
|
if FBGRA <> nil then
|
|
begin
|
|
if not FDiscardedRect.IsEmpty then
|
|
begin
|
|
FBGRA.ClipRect := FDiscardedRect;
|
|
FDiscardedRect := EmptyRect;
|
|
RedrawBitmapContent;
|
|
FBGRA.NoClip;
|
|
end;
|
|
FBGRA.Draw(Canvas, rect(0, 0, ClientWidth, ClientHeight));
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.Resize;
|
|
begin
|
|
inherited Resize;
|
|
if (FBGRA <> nil) and ((ClientWidth <> FBGRA.Width) or (ClientHeight <> FBGRA.Height)) then
|
|
DiscardBitmap;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.BGRASetSize(AWidth, AHeight: integer);
|
|
begin
|
|
if (FBGRA <> nil) and ((AWidth <> FBGRA.Width) or (AHeight <> FBGRA.Height)) then
|
|
begin
|
|
FBGRA.SetSize(AWidth, AHeight);
|
|
RedrawBitmapContent;
|
|
FDiscardedRect := EmptyRect;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.RedrawBitmapContent;
|
|
var
|
|
ARect: TRect;
|
|
TS: TTextStyle;
|
|
scale: Double;
|
|
begin
|
|
if (FBGRA <> nil) and (FBGRA.NbPixels <> 0) then
|
|
begin
|
|
FBGRA.FillRect(FBGRA.ClipRect, ColorToRGB(Color));
|
|
|
|
scale := BitmapScale;
|
|
ARect := GetClientRect;
|
|
ARect.Left := round(ARect.Left*scale);
|
|
ARect.Top := round(ARect.Top*scale);
|
|
ARect.Right := round(ARect.Right*scale);
|
|
ARect.Bottom := round(ARect.Bottom*scale);
|
|
|
|
// if BevelOuter is set then draw a frame with BevelWidth
|
|
if BevelOuter <> bvNone then
|
|
FBGRA.CanvasBGRA.Frame3d(ARect, round(BevelWidth*scale), BevelOuter,
|
|
BGRA(255, 255, 255, 200), BGRA(0, 0, 0, 160)); // Note: Frame3D inflates ARect
|
|
|
|
InflateRect(ARect, -round(BorderWidth*scale), -round(BorderWidth*scale));
|
|
|
|
// if BevelInner is set then skip the BorderWidth and draw a frame with BevelWidth
|
|
if BevelInner <> bvNone then
|
|
FBGRA.CanvasBGRA.Frame3d(ARect, round(BevelWidth*scale), BevelInner,
|
|
BGRA(255, 255, 255, 160), BGRA(0, 0, 0, 160)); // Note: Frame3D inflates ARect
|
|
|
|
if Caption <> '' then
|
|
begin
|
|
FBGRA.CanvasBGRA.Font.Assign(Canvas.Font);
|
|
FBGRA.CanvasBGRA.Font.Height:= round(FBGRA.CanvasBGRA.Font.Height*scale);
|
|
{$IFDEF FPC}//#
|
|
TS := Canvas.TextStyle;
|
|
{$ENDIF}
|
|
TS.Alignment := Alignment;
|
|
TS.Layout := tlTop;
|
|
TS.Opaque := False;
|
|
TS.Clipping := False;
|
|
{$IFDEF FPC}//#
|
|
TS.SystemFont := Canvas.Font.IsDefault;
|
|
{$ENDIF}
|
|
|
|
FBGRA.CanvasBGRA.Font.Color := Color xor $FFFFFF;
|
|
|
|
if not Enabled then
|
|
FBGRA.CanvasBGRA.Font.Style := [fsStrikeOut]
|
|
else
|
|
FBGRA.CanvasBGRA.Font.Style := [];
|
|
|
|
FBGRA.CanvasBGRA.TextRect(ARect, ARect.Left, ARect.Top, Caption, TS);
|
|
end;
|
|
|
|
if Assigned(FOnRedraw) then
|
|
FOnRedraw(self, FBGRA);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.SetColor(Value: TColor);
|
|
begin
|
|
if Value <> Color then
|
|
DiscardBitmap;
|
|
|
|
{$IFDEF FPC}
|
|
inherited SetColor(Value);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$hints off}
|
|
procedure TCustomBGRAVirtualScreen.WMEraseBkgnd(var Message: {$IFDEF FPC}TLMEraseBkgnd{$ELSE}TWMEraseBkgnd{$ENDIF});
|
|
begin
|
|
//do nothing
|
|
end;
|
|
|
|
{$hints on}
|
|
|
|
procedure TCustomBGRAVirtualScreen.SetEnabled(Value: boolean);
|
|
begin
|
|
if Value <> Enabled then
|
|
DiscardBitmap;
|
|
inherited SetEnabled(Value);
|
|
end;
|
|
|
|
constructor TCustomBGRAVirtualScreen.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
inherited BevelOuter := bvNone;
|
|
FBGRA := TBGRABitmap.Create;
|
|
FBitmapAutoScale := true;
|
|
FBevelWidth := 1;
|
|
FAlignment := taLeftJustify;
|
|
FDiscardedRect := EmptyRect;
|
|
Color := clWhite;
|
|
end;
|
|
|
|
function TCustomBGRAVirtualScreen.BitmapRectToClient(ARect: TRect): TRect;
|
|
var
|
|
scale: Double;
|
|
begin
|
|
scale := BitmapScale;
|
|
result := rect(floor(ARect.Left/scale), floor(ARect.Top/scale),
|
|
ceil(ARect.Right/scale), ceil(ARect.Bottom/scale));
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.RedrawBitmap;
|
|
begin
|
|
RedrawBitmapContent;
|
|
FDiscardedRect := EmptyRect;
|
|
Repaint;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.RedrawBitmap(ARect: TRect);
|
|
var
|
|
All, displayRect: TRect;
|
|
begin
|
|
if Assigned(FBGRA) then
|
|
begin
|
|
All := Rect(0,0,FBGRA.Width,FBGRA.Height);
|
|
ARect.Intersect(All);
|
|
if not FDiscardedRect.IsEmpty then
|
|
begin
|
|
if ARect.IsEmpty then
|
|
ARect := FDiscardedRect
|
|
else
|
|
ARect.Union(FDiscardedRect);
|
|
FDiscardedRect := EmptyRect;
|
|
end;
|
|
if ARect.IsEmpty then exit;
|
|
if ARect.Contains(All) then
|
|
begin
|
|
RedrawBitmap;
|
|
end
|
|
else
|
|
begin
|
|
FBGRA.ClipRect := ARect;
|
|
RedrawBitmapContent;
|
|
FBGRA.NoClip;
|
|
displayRect := BitmapRectToClient(ARect);
|
|
{$IFDEF LINUX}
|
|
FBGRA.DrawPart(ARect, Canvas, displayRect, True);
|
|
{$ELSE}
|
|
InvalidateRect(Handle, @displayRect, False);
|
|
Update;
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.RedrawBitmap(ARectArray: array of TRect);
|
|
const cellShift = 6;
|
|
cellSize = 1 shl cellShift;
|
|
var
|
|
grid: array of array of boolean;
|
|
gAll: TRect;
|
|
|
|
procedure IncludeRect(ARect: TRect);
|
|
var
|
|
gR: TRect;
|
|
y,x: LongInt;
|
|
begin
|
|
with ARect do
|
|
gR := rect(max(Left,0) shr cellShift, max(Top,0) shr cellShift,
|
|
(max(Right,0)+cellSize-1) shr cellShift,
|
|
(max(Bottom,0)+cellSize-1) shr cellShift);
|
|
gR.Intersect(gAll);
|
|
if gR.IsEmpty then exit;
|
|
|
|
for y := gR.Top to gR.Bottom-1 do
|
|
for x := gR.Left to gR.Right-1 do
|
|
grid[y,x] := true;
|
|
end;
|
|
|
|
var
|
|
gW,gH, i,gCount: integer;
|
|
gR: TRect;
|
|
y,x: LongInt;
|
|
expand: boolean;
|
|
|
|
|
|
begin
|
|
if not Assigned(FBGRA) then exit;
|
|
gW := (Bitmap.Width+cellSize-1) shr cellShift;
|
|
gH := (Bitmap.Height+cellSize-1) shr cellShift;
|
|
gAll := rect(0,0,gW,gH);
|
|
|
|
//determine which cells of the grid to redraw
|
|
setlength(grid,gH,gW);
|
|
for i := 0 to high(ARectArray) do
|
|
IncludeRect(ARectArray[i]);
|
|
if not FDiscardedRect.IsEmpty then
|
|
begin
|
|
IncludeRect(FDiscardedRect);
|
|
FDiscardedRect := EmptyRect;
|
|
end;
|
|
|
|
gCount := 0;
|
|
for y := 0 to gH-1 do
|
|
for x := 0 to gW-1 do
|
|
if grid[y,x] then inc(gCount);
|
|
|
|
if gCount >= gH*gW div 5 then
|
|
begin
|
|
RedrawBitmap(rect(0,0,Width,Height));
|
|
end else
|
|
for y := 0 to gH-1 do
|
|
begin
|
|
x := 0;
|
|
while x < gW do
|
|
begin
|
|
if grid[y,x] then
|
|
begin
|
|
gR.Left := x;
|
|
grid[y,x] := false;
|
|
inc(x);
|
|
while (x < gW) and grid[y,x] do
|
|
begin
|
|
grid[y,x] := false;
|
|
inc(x);
|
|
end;
|
|
gR.Right := x;
|
|
gR.Top := y;
|
|
gR.Bottom := y+1;
|
|
expand := true;
|
|
while expand and (gR.Bottom < gH) do
|
|
begin
|
|
expand := true;
|
|
for x := gR.Left to gR.Right-1 do
|
|
if not grid[gR.Bottom, x] then
|
|
begin
|
|
expand := false;
|
|
break;
|
|
end;
|
|
if expand then
|
|
begin
|
|
for x := gR.Left to gR.Right-1 do
|
|
grid[gR.Bottom,x] := false;
|
|
inc(gR.Bottom);
|
|
end;
|
|
end;
|
|
|
|
RedrawBitmap(rect(gR.Left shl cellShift,gR.Top shl cellShift,gr.Right shl cellShift,gr.Bottom shl cellShift));
|
|
end else
|
|
inc(x);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.DiscardBitmap;
|
|
begin
|
|
if FBGRA <> nil then
|
|
DiscardBitmap(rect(0,0,FBGRA.Width,FBGRA.Height));
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.DiscardBitmap(ARect: TRect);
|
|
var
|
|
displayRect: TRect;
|
|
begin
|
|
ARect.Intersect(rect(0,0,FBGRA.Width,FBGRA.Height));
|
|
if ARect.IsEmpty then exit;
|
|
if FBGRA <> nil then
|
|
begin
|
|
if FDiscardedRect.IsEmpty then
|
|
FDiscardedRect := ARect
|
|
else
|
|
FDiscardedRect.Union(ARect);
|
|
displayRect := BitmapRectToClient(ARect);
|
|
InvalidateRect(self.Handle, @displayRect, false);
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomBGRAVirtualScreen.InvalidateBitmap(ARect: TRect);
|
|
var
|
|
displayRect: TRect;
|
|
begin
|
|
displayRect := BitmapRectToClient(ARect);
|
|
InvalidateRect(self.Handle, @displayRect, false);
|
|
end;
|
|
|
|
destructor TCustomBGRAVirtualScreen.Destroy;
|
|
begin
|
|
FBGRA.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
end.
|