TAChart: Improved drawing speed of TColorMapSeries

This commit is contained in:
wp_xyz 2023-03-19 11:49:36 +01:00
parent 45d350bfd2
commit 6cebac6b7b
2 changed files with 95 additions and 45 deletions

View File

@ -18,7 +18,7 @@ unit TAFuncSeries;
interface interface
uses uses
Classes, Graphics, typ, Types, Classes, Graphics, typ, Types, IntfGraphics,
TAChartUtils, TACustomFuncSeries, TACustomSeries, TACustomSource, TASources, TAChartUtils, TACustomFuncSeries, TACustomSeries, TACustomSource, TASources,
TADrawUtils, TAFitUtils, TALegend, TATypes, TAFitLib, TAStyles, TAColorMap; TADrawUtils, TAFitUtils, TALegend, TATypes, TAFitLib, TAStyles, TAColorMap;
@ -421,6 +421,7 @@ type
FStepX: TFuncSeriesStep; FStepX: TFuncSeriesStep;
FStepY: TFuncSeriesStep; FStepY: TFuncSeriesStep;
FUseImage: TUseImage; FUseImage: TUseImage;
FBufferImage: TLazIntfImage;
function GetBuiltinColorSource: TListChartSource; function GetBuiltinColorSource: TListChartSource;
function GetBuiltinPalette: TColorMapPalette; function GetBuiltinPalette: TColorMapPalette;
function GetColorSource: TCustomChartSource; function GetColorSource: TCustomChartSource;
@ -441,9 +442,11 @@ type
procedure SetUseImage(AValue: TUseImage); procedure SetUseImage(AValue: TUseImage);
protected protected
FMinZ, FMaxZ: Double; FMinZ, FMaxZ: Double;
function BufferImageValid: Boolean;
procedure GetLegendItems(AItems: TChartLegendItems); override; procedure GetLegendItems(AItems: TChartLegendItems); override;
procedure GetZRange(ARect: TRect; dx, dy: Integer); procedure GetZRange(ARect: TRect; dx, dy: Integer);
class procedure GetXYCountNeeded(out AXCount, AYCount: Cardinal); virtual; class procedure GetXYCountNeeded(out AXCount, AYCount: Cardinal); virtual;
procedure InvalidateBufferImage;
property BuiltinColorSource: TListChartSource read GetBuiltinColorSource; property BuiltinColorSource: TListChartSource read GetBuiltinColorSource;
public public
@ -452,8 +455,9 @@ type
procedure Assign(ASource: TPersistent); override; procedure Assign(ASource: TPersistent); override;
public public
function FunctionValue(AX, AY: Double): Double; virtual; procedure ClipRectChanged; override;
procedure Draw(ADrawer: IChartDrawer); override; procedure Draw(ADrawer: IChartDrawer); override;
function FunctionValue(AX, AY: Double): Double; virtual;
function IsEmpty: Boolean; override; function IsEmpty: Boolean; override;
published published
property AxisIndexX; property AxisIndexX;
@ -501,7 +505,7 @@ implementation
uses uses
{$IF FPC_FullVersion >= 30101}ipf{$ELSE}ipf_fix{$ENDIF}, {$IF FPC_FullVersion >= 30101}ipf{$ELSE}ipf_fix{$ENDIF},
GraphType, GraphUtil, IntfGraphics, Math, spe, StrUtils, SysUtils, GraphType, GraphUtil, Math, spe, StrUtils, SysUtils,
TAChartStrConsts, TAGeometry, TAGraph, TAMath; TAChartStrConsts, TAGeometry, TAGraph, TAMath;
const const
@ -2474,6 +2478,7 @@ end;
destructor TCustomColorMapSeries.Destroy; destructor TCustomColorMapSeries.Destroy;
begin begin
FreeAndNil(FBufferImage);
FreeAndNil(FColorMap); FreeAndNil(FColorMap);
FreeAndNil(FBrush); FreeAndNil(FBrush);
inherited Destroy; inherited Destroy;
@ -2491,6 +2496,20 @@ begin
inherited Assign(ASource); inherited Assign(ASource);
end; end;
{ Returns whether there exists a buffer image of the color map at the moment. }
function TCustomColorMapSeries.BufferImageValid: Boolean;
begin
Result := Assigned(FBufferImage);
end;
{ Is called by the chart when it detects a change in the clip rect (series area).
The ColorMapSeries must recalculate the buffer image afterwards in the
next paint event. }
procedure TCustomColorMapSeries.ClipRectChanged;
begin
InvalidateBufferImage;
end;
procedure TCustomColorMapSeries.Draw(ADrawer: IChartDrawer); procedure TCustomColorMapSeries.Draw(ADrawer: IChartDrawer);
var var
ext: TDoubleRect; ext: TDoubleRect;
@ -2500,7 +2519,6 @@ var
pt, next, offset: TPoint; pt, next, offset: TPoint;
gp: TDoublePoint; gp: TDoublePoint;
v: Double; v: Double;
img: TLazIntfImage = nil;
rawImage: TRawImage; rawImage: TRawImage;
optimize: Boolean; optimize: Boolean;
x, y: Integer; x, y: Integer;
@ -2528,8 +2546,18 @@ begin
cmuiNever: optimize := false; cmuiNever: optimize := false;
end; end;
if optimize then if optimize then
img := CreateLazIntfImage(rawImage, r.BottomRight - r.TopLeft) begin
else begin if BufferImageValid then
begin
ADrawer.PutImage(r.Left, r.Top, FBufferImage);
exit;
end else
begin
FBufferImage.Free;
FBufferImage := CreateLazIntfImage(rawImage, r.BottomRight - r.TopLeft)
end;
end else
begin
ADrawer.Brush := Brush; ADrawer.Brush := Brush;
ADrawer.SetPenParams(psClear, clTAColor); ADrawer.SetPenParams(psClear, clTAColor);
end; end;
@ -2544,50 +2572,45 @@ begin
exit; exit;
end; end;
try pt.Y := (r.Top div scaled_stepY - 1) * scaled_stepY + offset.Y mod scaled_stepY;
pt.Y := (r.Top div scaled_stepY - 1) * scaled_stepY + offset.Y mod scaled_stepY; while pt.Y <= r.Bottom do begin
while pt.Y <= r.Bottom do begin next.Y := pt.Y + scaled_stepY;
next.Y := pt.Y + scaled_stepY; if next.Y <= r.Top then begin
if next.Y <= r.Top then begin pt.Y := next.Y;
pt.Y := next.Y; continue;
end;
pt.X := (r.Left div scaled_stepX - 1) * scaled_stepX + offset.X mod scaled_stepX;
while pt.X <= r.Right do begin
next.X := pt.X + scaled_stepX;
if next.X <= r.Left then begin
pt.X := next.X;
continue; continue;
end; end;
pt.X := (r.Left div scaled_stepX - 1) * scaled_stepX + offset.X mod scaled_stepX; gp := GraphToAxis(ParentChart.ImageToGraph((pt + next) div 2));
while pt.X <= r.Right do begin v := FunctionValue(gp.X, gp.Y);
next.X := pt.X + scaled_stepX; cell := Rect(
if next.X <= r.Left then begin Max(pt.X, r.Left), Max(pt.Y, r.Top),
pt.X := next.X; Min(next.X, r.Right) + 1, Min(next.Y, r.Bottom) + 1);
continue; if optimize then begin
end; if ColorSource = nil then
gp := GraphToAxis(ParentChart.ImageToGraph((pt + next) div 2)); cellColor := Brush.Color
// if not (csDesigning in ComponentState) then else
v := FunctionValue(gp.X, gp.Y); cellColor := FColorMap.ColorByValue(v);
cell := Rect( for y := cell.Top - r.Top to cell.Bottom - r.Top - 2 do
Max(pt.X, r.Left), Max(pt.Y, r.Top), for x := cell.Left - r.Left to cell.Right - r.Left - 2 do
Min(next.X, r.Right) + 1, Min(next.Y, r.Bottom) + 1); FBufferImage.TColors[x, y] := cellColor;
if optimize then begin end
if ColorSource = nil then else begin
cellColor := Brush.Color if ColorSource <> nil then
else ADrawer.BrushColor := FColorMap.ColorByValue(v);
cellColor := FColorMap.ColorByValue(v); ADrawer.Rectangle(cell);
for y := cell.Top - r.Top to cell.Bottom - r.Top - 2 do
for x := cell.Left - r.Left to cell.Right - r.Left - 2 do
img.TColors[x, y] := cellColor;
end
else begin
if ColorSource <> nil then
ADrawer.BrushColor := FColorMap.ColorByValue(v);
ADrawer.Rectangle(cell);
end;
pt.X := next.X;
end; end;
pt.Y := next.Y; pt.X := next.X;
end; end;
if optimize then pt.Y := next.Y;
ADrawer.PutImage(r.Left, r.Top, img);
finally
FreeAndNil(img);
end; end;
if optimize and BufferImageValid then
ADrawer.PutImage(r.Left, r.Top, FBufferImage);
end; end;
function TCustomColorMapSeries.FunctionValue(AX, AY: Double): Double; function TCustomColorMapSeries.FunctionValue(AX, AY: Double): Double;
@ -2742,6 +2765,14 @@ begin
end; end;
end; end;
{ Destroys the current buffer image because some parameter affecting it has
changed. The buffer image will be recreated when the series is painted the
next time. }
procedure TCustomColorMapSeries.InvalidateBufferImage;
begin
FreeAndNil(FBufferImage);
end;
function TCustomColorMapSeries.IsEmpty: Boolean; function TCustomColorMapSeries.IsEmpty: Boolean;
begin begin
Result := true; Result := true;
@ -2766,33 +2797,40 @@ procedure TCustomColorMapSeries.SetBrush(AValue: TBrush);
begin begin
if FBrush = AValue then exit; if FBrush = AValue then exit;
FBrush := AValue; FBrush := AValue;
InvalidateBufferImage;
UpdateParentChart; UpdateParentChart;
end; end;
procedure TCustomColorMapSeries.SetBuiltinPalette(AValue: TColorMapPalette); procedure TCustomColorMapSeries.SetBuiltinPalette(AValue: TColorMapPalette);
begin begin
InvalidateBufferImage;
FColorMap.BuiltinPalette := AValue; FColorMap.BuiltinPalette := AValue;
UpdateParentChart;
end; end;
procedure TCustomColorMapSeries.SetColorSource(AValue: TCustomChartSource); procedure TCustomColorMapSeries.SetColorSource(AValue: TCustomChartSource);
begin begin
InvalidateBufferImage;
FColorMap.ColorSource := AValue; FColorMap.ColorSource := AValue;
end; end;
procedure TCustomColorMapSeries.SetInterpolate(AValue: Boolean); procedure TCustomColorMapSeries.SetInterpolate(AValue: Boolean);
begin begin
if GetInterpolate = AValue then exit; if GetInterpolate = AValue then exit;
InvalidateBufferImage;
FColorMap.Interpolate := AValue; FColorMap.Interpolate := AValue;
UpdateParentChart; UpdateParentChart;
end; end;
procedure TCustomColorMapSeries.SetPaletteMax(AValue: Double); procedure TCustomColorMapSeries.SetPaletteMax(AValue: Double);
begin begin
InvalidateBufferImage;
FColorMap.PaletteMax := AValue; FColorMap.PaletteMax := AValue;
end; end;
procedure TCustomColorMapSeries.SetPaletteMin(AValue: Double); procedure TCustomColorMapSeries.SetPaletteMin(AValue: Double);
begin begin
InvalidateBufferImage;
FColorMap.PaletteMin := AValue; FColorMap.PaletteMin := AValue;
end; end;
@ -2800,6 +2838,7 @@ procedure TCustomColorMapSeries.SetStepX(AValue: TFuncSeriesStep);
begin begin
if FStepX = AValue then exit; if FStepX = AValue then exit;
FStepX := AValue; FStepX := AValue;
InvalidateBufferImage;
UpdateParentChart; UpdateParentChart;
end; end;
@ -2807,6 +2846,7 @@ procedure TCustomColorMapSeries.SetStepY(AValue: TFuncSeriesStep);
begin begin
if FStepY = AValue then exit; if FStepY = AValue then exit;
FStepY := AValue; FStepY := AValue;
InvalidateBufferImage;
UpdateParentChart; UpdateParentChart;
end; end;
@ -2814,6 +2854,7 @@ procedure TCustomColorMapSeries.SetUseImage(AValue: TUseImage);
begin begin
if FUseImage = AValue then exit; if FUseImage = AValue then exit;
FUseImage := AValue; FUseImage := AValue;
InvalidateBufferImage;
UpdateParentChart; UpdateParentChart;
end; end;
@ -2846,6 +2887,7 @@ procedure TColorMapSeries.SetOnCalculate(AValue: TFuncCalculate3DEvent);
begin begin
if TMethod(FOnCalculate) = TMethod(AValue) then exit; if TMethod(FOnCalculate) = TMethod(AValue) then exit;
FOnCalculate := AValue; FOnCalculate := AValue;
InvalidateBufferImage;
UpdateParentChart; UpdateParentChart;
end; end;

View File

@ -75,6 +75,7 @@ type
destructor Destroy; override; destructor Destroy; override;
public public
procedure ClipRectChanged; virtual; virtual;
procedure Draw(ADrawer: IChartDrawer); virtual; abstract; procedure Draw(ADrawer: IChartDrawer); virtual; abstract;
function GetAxisBounds(AAxis: TChartAxis; out AMin, AMax: Double): boolean; virtual; abstract; function GetAxisBounds(AAxis: TChartAxis; out AMin, AMax: Double): boolean; virtual; abstract;
function GetGraphBounds: TDoubleRect; virtual; abstract; function GetGraphBounds: TDoubleRect; virtual; abstract;
@ -835,6 +836,8 @@ procedure TChart.DisplaySeries(ADrawer: IChartDrawer);
begin begin
try try
ADrawer.SetTransparency(ATransparency); ADrawer.SetTransparency(ATransparency);
if FClipRect <> FOldClipRect then
ASeries.ClipRectChanged;
ASeries.Draw(ADrawer); ASeries.Draw(ADrawer);
except except
ASeries.Active := false; ASeries.Active := false;
@ -2052,6 +2055,11 @@ begin
// empty // empty
end; end;
procedure TBasicChartSeries.ClipRectChanged;
begin
// empty
end;
destructor TBasicChartSeries.Destroy; destructor TBasicChartSeries.Destroy;
begin begin
if FChart <> nil then if FChart <> nil then