All this sounds reasonable. But without an example it is risky. While you immediately can use the canvas' Pen or Brush for drawing, the Font needs a special treatment: it is created outside the canvas: https://wiki.freepascal.org/fcl-image#Drawing_text. Not sure if this is good example and applies also to the FAssignedFont. But I'd be careful... Do you remember the similar case with the regions?
I don't remember, but that's ok. It's great that you remember because you have the opportunity to fix the following bugs.
1/2 TLazCanvasState = class
public
Brush: TFPCustomBrush;
Pen: TFPCustomPen;
Font: TFPCustomFont;
BaseWindowOrg: TPoint;
WindowOrg: TPoint;
Clipping: Boolean;
ClipRegion: TFPCustomRegion;
destructor Destroy; override;
end;
destructor TLazCanvasState.Destroy;
begin
Brush.Free;
Pen.Free;
Font.Free;
inherited Destroy;
end;
function TLazCanvas.SaveState: Integer;
var
lState: TLazCanvasState;
begin
lState := TLazCanvasState.Create;
lState.Brush := Brush.CopyBrush;
lState.Pen := Pen.CopyPen;
lState.Font := Font.CopyFont;
lState.BaseWindowOrg := BaseWindowOrg;
lState.WindowOrg := WindowOrg;
lState.Clipping := Clipping;
Result := GraphicStateList.Add(lState);
end;
procedure TLazCanvas.RestoreState(AIndex: Integer);
var
lState: TLazCanvasState;
begin
if AIndex < 0 then AIndex := AIndex + GraphicStateList.Count;
lState := TLazCanvasState(GraphicStateList.Items[AIndex]);
GraphicStateList.Delete(AIndex);
if lState = nil then Exit;
AssignPenData(lState.Pen);
AssignBrushData(lState.Brush);
AssignFontData(lState.Font);
BaseWindowOrg := lState.BaseWindowOrg;
WindowOrg := lState.WindowOrg;
Clipping := lState.Clipping;
lState.Free;
end;
Notice that SaveState and RestoreState ignore TLazCanvasState.ClipRegion. Saving/Restoring the Clipping value and ignoring the ClipRegion can't be good.2/2 Regarding the addition of the FAssignedFont.Free line in destructor TLazCanvas.destroy, let's wait until:a) FPC issue regarding DoCopyProps routines in fcl-image package is fixed. This bug is also related to the LazCanvas comment:
// Utilized by LCLIntf.SelectObject and by RestoreState
// This needed to be added because Pen/Brush.Assign raises exceptions
https://gitlab.com/freepascal.org/fpc/source/-/issues/40362b) Somebody will do something about Saving/Restoring the Clipping value in TLazCanvas, as was written at the beginning of this reply.
c) TCDWidgetSet.RectVisible always returns true, which is a bug. The function should mimic TQtWidgetSet.RectVisible, but it appears that the qt function might also be buggy when QtDC.getClipping returns false. I don't have qt installed so I can't check to see if TQtWidgetSet.RectVisible returns the same results as TGtk2WidgetSet.RectVisible.
d) Fix procedure TLazCanvas.ResetCanvasState, because this procedure is expected to be very important for customdrawn.
After the above four fixes, we'll see if variables have been included in TLazCanvas just as workarounds for "
// Routines broken/unimplemented/incompatible in FPC", that's another comment found in the same file, regarding TLazCanvas.
Looking at the code of
GetAssignedBrush/Pen/Font, I'm sure there are bugs. A recommended practice is that the entity that creates or allocates a block of memory should be responsible with freeing it. TLazCanvas creates brushes, pens and fonts without keeping accountability, and also tries to free a single instance of pen and brush, ignoring completely the fonts.
procedure TForm1.Button1Click(Sender: TObject);
//add to uses lazcanvas and fpcanvas and then notice the 100*3 memory leaks
var
lc:TLazCanvas;
i:integer;
fb: TFPCustomBrush;
ff: TFPCustomFont;
fp: TFPCustomPen;
begin
lc := TLazCanvas.create(nil);
for i:=0 to 99 do
begin
fb := lc.AssignedBrush;
ff := lc.AssignedFont;
fp := lc.AssignedPen;
end;
lc.Free;
{Using Heaptrc unit (-gh)
1256 memory blocks allocated : 1610996/1612008
956 memory blocks freed : 1566196/1567208
300 unfreed memory blocks : 44800
True heap size : 1605632
True free heap : 1496832
Should be : 1503232}
end;