BGRABitmap tutorial TAChart
│
English (en) │
français (fr) │
You can make beautiful charts using BGRABitmap. First, add TAChartBGRA package with the project inspector. Maybe the package will not appear in the list or will not be installed. In this case, open it manually with the menu Package, Open package file (.lpk). It must be in component\tachart.
Updating TABGRAUtils unit
In the package window, double-clic on tabgrautils.pas. In the code editor, remplace the content. For example, select all with CTRL-A and then paste this:
{
*****************************************************************************
* *
* See the file COPYING.modifiedLGPL.txt, included in this distribution, *
* for details about the copyright. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
*****************************************************************************
Author: circular
}
unit TABGRAUtils;
interface
{$H+}
uses
BGRABitmap, BGRABitmapTypes, BGRAGradients, Graphics, Types,
TASeries;
function CreateChocolateBar(AColor: TBGRAPixel; ALightPos: TPoint; ARect: TRect;
ABorder: integer; ARoundedCorners: Boolean; AOptions: TRectangleMapOptions): TBGRABitmap;
procedure DrawChocolateBar(ASeries: TBarSeries; ACanvas: TCanvas; ARect: TRect; APointIndex: integer; ARounded: boolean);
function CreatePhong3DBar(AColor: TBGRAPixel; ALightPos: TPoint; var ARect: TRect;
ADepth: Integer): TBGRABitmap;
procedure DrawPhong3DBar(ASeries: TBarSeries; ACanvas: TCanvas; ARect: TRect; APointIndex: integer);
implementation
uses
TAChartUtils, TAGeometry;
function CreateChocolateBar(AColor: TBGRAPixel; ALightPos: TPoint; ARect: TRect;
ABorder: integer; ARoundedCorners: Boolean; AOptions: TRectangleMapOptions): TBGRABitmap;
var
phong: TPhongShading;
t: TPoint;
begin
t := MaxPoint(ARect.BottomRight - ARect.TopLeft, Point(0, 0));
Result := TBGRABitmap.Create(t.X, t.Y);
if (t.X = 0) and (t.Y = 0) then exit;
if ABorder < 0 then ABorder := 0;
phong := TPhongShading.Create;
try
phong.AmbientFactor := 0.5;
phong.LightPosition := ALightPos - ARect.TopLeft;
phong.DrawRectangle(Result, BoundsSize(0, 0, t), ABorder, ABorder,
AColor, ARoundedCorners, AOptions);
finally
phong.Free;
end;
end;
procedure DrawChocolateBar(ASeries: TBarSeries; ACanvas: TCanvas; ARect: TRect; APointIndex: integer; ARounded: boolean);
var
bar: TBGRABitmap;
border: integer;
color,pointColor : TColor;
begin
border := (ARect.Right-ARect.Left) div 8;
ARect.Top += -border div 2 +1;
ARect.Bottom += border div 2 +1;
color := ASeries.BarBrush.Color;
pointColor := ASeries.Source[APointIndex]^.Color;
if pointColor <> clTAColor then color := pointColor;
bar := CreateChocolateBar(ColorToBGRA(ColorToRGB(color),255-ASeries.Transparency),
Point(ASeries.ParentChart.ClientWidth div 2, 0),
ARect, border, ARounded, []);
try
with ARect.TopLeft do
bar.Draw(ACanvas, X, Y, false);
finally
bar.Free;
end;
end;
function CreatePhong3DBar(AColor: TBGRAPixel; ALightPos: TPoint; var ARect: TRect;
ADepth: Integer): TBGRABitmap;
var
phong: TPhongShading;
i: Integer;
map: TBGRABitmap;
h: TBGRAPixel;
t: TPoint;
begin
t := MaxPoint(ARect.BottomRight - ARect.TopLeft, Point(0, 0));
map := TBGRABitmap.Create(t.X + ADepth,t.Y + ADepth);
try
map.FillRect(0, ADepth, t.X, t.Y + ADepth, BGRAWhite, dmSet);
for i := 1 to ADepth do begin
h := MapHeightToBGRA((ADepth - i) / ADepth, 255);
map.SetHorizLine(i, ADepth - i, t.X - 1 + i - 1, h);
map.SetVertLine(t.X - 1 + i, ADepth - i, t.Y + ADepth - 1 - i, h);
end;
Result := TBGRABitmap.Create(t.X + ADepth, t.Y + ADepth);
ARect.Top -= ADepth;
ARect.Right += ADepth;
if (Result.width = 0) or (Result.Height = 0) then exit;
phong := TPhongShading.Create;
try
phong.AmbientFactor := 0.5;
phong.LightPosition := ALightPos - ARect.TopLeft;
phong.Draw(Result, map, ADepth, 0, 0, AColor);
finally
phong.Free;
end;
finally
map.Free;
end;
end;
procedure DrawPhong3DBar(ASeries: TBarSeries; ACanvas: TCanvas; ARect: TRect; APointIndex: integer);
procedure DrawContour(var ABar: TBGRABitmap; var ADrawnRect: TRect);
var
size: TPoint;
temp: TBGRABitmap;
marginValue, depth: integer;
margin: TPoint;
begin
margin := point(0, 0);
if ASeries.BarPen.Style = psClear then exit;
size := ARect.BottomRight - ARect.TopLeft;
if ASeries.BarPen.Width > 1 then begin
marginValue := (ASeries.BarPen.Width + 1) div 2;
margin := Point(marginValue, marginValue);
temp := TBGRABitmap.Create(ABar.Width + 2 * margin.X, ABar.Height + 2 * margin.Y);
temp.PutImage(margin.X, margin.Y, ABar, dmSet);
BGRAReplace(ABar, temp);
ADrawnRect.TopLeft -= margin;
ADrawnRect.BottomRight += margin;
end;
depth := ASeries.Depth;
with ABar.CanvasBGRA do begin
Pen.Assign(ASeries.BarPen);
Brush.Style := bsClear;
Polygon([
Point(margin.x + 0, margin.y + depth),
Point(margin.x + depth, margin.y + 0),
Point(margin.x + size.x - 1 + depth, margin.y + 0),
Point(margin.x + size.x - 1 + depth, margin.y + size.y - 1),
Point(margin.x + size.x - 1, margin.y + size.y - 1 + depth),
Point(margin.x + 0, margin.y + size.y - 1 + depth)
]);
end;
end;
var
bar: TBGRABitmap;
color,pointColor : TColor;
drawnRect: TRect;
begin
color := ASeries.BarBrush.Color;
pointColor := ASeries.Source[APointIndex]^.Color;
if pointColor <> clTAColor then color := pointColor;
drawnRect := ARect;
bar := CreatePhong3DBar(ColorToBGRA(ColorToRGB(color),255-ASeries.Transparency),
Point(ASeries.ParentChart.ClientWidth div 2, 0),
drawnRect, ASeries.Depth);
try
DrawContour(bar, drawnRect);
with drawnRect.TopLeft do
bar.Draw(ACanvas, X, Y, false);
finally
bar.Free;
end;
end;
end.
Then click on the button to install the package.
Add a TAChart
TAChart in fact has the classname TChart. To add a chart, browse the components, choose Chart tab and click on TChart icon. Click and draw a rectangle on the window to drop it.
In order to supply data, click on TRandomChartSource component icon and click once in the window to drop it. In the Object Inspector, define PointsNumber to 10, XMax to 10 and YMax to 10.
Now let's create a series that uses this source. To do that, click on your chart and in the Object Inspector, go to Series property. Click on the three dots, and add a Bar series. In its properties, go to Source property and choose RandomChartSource1. The chart should be filled with red bars.
As we are going to do some 3D, we need some depth. So set the Depth property of the series to 10.
Finally, click on the chart and set the Align property to alClient in order to fill the window.
Decorate in normal rendering mode
Go to the Series property of your chart. Choose the bar series, and in the Object Inspector, go to the events and define OnBeforeDrawBar. Add the following code:
uses TABGRAUtils;
procedure TForm1.BeforeDrawBarHandler(ASender: TBarSeries; ACanvas: TCanvas;
const ARect: TRect; APointIndex, AStackIndex: Integer;
var ADoDefaultDrawing: Boolean);
begin
ADoDefaultDrawing:= false;
DrawPhong3DBar(ASender, ACanvas, ARect, APointIndex);
end;
Then run the program.
Your chart should look like this:
You can also use the DrawChocolateBar function. Set PointsNumber property of RandomChartSource1 to 5. Change the code of the OnBeforeDrawBarEvent to:
uses TABGRAUtils;
procedure TForm1.BeforeDrawBarHandler(ASender: TBarSeries; ACanvas: TCanvas;
const ARect: TRect; APointIndex, AStackIndex: Integer;
var ADoDefaultDrawing: Boolean);
begin
ADoDefaultDrawing:= false;
DrawChocolateBar(ASender, ACanvas, ARect, APointIndex, True);
end;
Using BGRABitmap to render the whole chart
Bars are not very different with or without antialiasing. Go to the Series property and remove the bars, add a Line series, and set its Source to RandomChartSource1. Then, set its property Depth to 10 and its SeriesColor property to clRed.
In order to change the rendering engine, look for the TChartGUIConnectorBGRA component in the Chart tab. Click to drop it on the chart. Then, click on the chart background and with the object inspector, set the GUIConnector property by choosing ChartGUIConnectorBGRA1. The rendering is now done with antialiasing, no need to start the program to see:
Drawing a chart on any Canvas
Simple drawing
First add the TAChartBGRA package. To render your chart, you can for example use a PaintBox. In the OnPaint event, write :
uses BGRABitmap, TADrawerBGRA;
procedure TForm1.PaintBox1Paint(Sender: TObject);
var
bmp: TBGRABitmap;
id: IChartDrawer;
rp: TChartRenderingParams;
begin
bmp := TBGRABitmap.Create(PaintBox1.Width, PaintBox1.Height);
Chart1.DisableRedrawing;
try
id := TBGRABitmapDrawer.Create(bmp);
id.DoGetFontOrientation := @CanvasGetFontOrientationFunc;
rp := Chart1.RenderingParams;
Chart1.Draw(id, Rect(0, 0, PaintBox1.Width, PaintBox1.Height));
Chart1.RenderingParams := rp;
bmp.Draw(PaintBox1.Canvas, 0, 0);
finally
Chart1.EnableRedrawing;
bmp.Free;
end;
end;
To avoid flickering, consider using TBGRAVirtualScreen from BGRAControls.
Text effects
To add text effects, you need the latest version of BGRABitmap, and the latest SVN of Lazarus. There is a FontRenderer property in each TBGRABitmap image, that you can define. You must not free the renderer, as it is automatically freed by the image.
The text effects are provided by the BGRATextFX unit that contains the TBGRATextEffectFontRenderer class. To add a golden contour to the letters and a shadow, add the following lines :
uses BGRATextFX, BGRAGradientScanner;
...
var
...
fontRenderer: TBGRATextEffectFontRenderer;
gold: TBGRAGradientScanner;
begin
...
fontRenderer:= TBGRATextEffectFontRenderer.Create;
fontRenderer.ShadowVisible := true; //adds a shadow
fontRenderer.OutlineVisible := true; //show the outline
gold := TBGRAGradientScanner.Create(CSSGold,CSSGoldenrod,gtLinear,PointF(0,0),PointF(20,20),true,true);
fontRenderer.OutlineTexture := gold; //define the texture used for the outline
fontRenderer.OuterOutlineOnly := true; //just the outer pixels of the text
bmp.FontRenderer := fontRenderer; //gives the FontRenderer to the image (which becomes the owner of the renderer)
...
gold.Free;
end;