Recent

Author Topic: TAchart, BeforeCustomDrawBackWall  (Read 325 times)

arneolav

  • Full Member
  • ***
  • Posts: 115
    • ElTranslador
TAchart, BeforeCustomDrawBackWall
« on: November 10, 2019, 09:56:18 pm »
Hi!
From TAChart Tutorial: Background design
If you have Lazarus version 1.9 or newer you many notice that the events OnBeforeDrawBackground and OnBeforeDrawBackWall are marked as deprecated, it is recommended to use OnBeforeCustomDrawBackground and OnBeforeCustomDrawBackWall. This was introduced because the new events support the TAChart drawing backend architecture which enables drawing on devices which do not have an LCL-compatible canvas (e.g. an OpenGL rendering context, or an SVG file). A chart drawer essentially provides the same commands as TCanvas, however, some differences exist here and there - unfortunately also in drawing of images and gradients: Images are requested as descendants of TFPCustomImage. And gradients are not supported at all, but may be provided after painting on an auxiliary bitmap.

An example from the wiki showing "GradientFill" does work ok, but i need no gradient, only any user selectable color color.

Code: Pascal  [Select]
  1. procedure MyChart.Chart_SensorBeforeCustomDrawBackWall(ASender: TChart;
  2.   ADrawer: IChartDrawer; const ARect: TRect; var ADoDefaultDrawing: Boolean);
  3. var
  4.   bmp: TBitmap;
  5.   img: TLazIntfImage;
  6. begin
  7.   img := TLazIntfImage.Create(0, 0);
  8.   try
  9.     bmp := TBitmap.Create;
  10.     try
  11.       bmp.SetSize(ARect.Right - ARect.Left, ARect.Bottom - ARect.Top);
  12.  
  13. This does work ok:
  14.      // bmp.Canvas.GradientFill(Rect(0, 0, bmp.Width, bmp.Height), clWhite, clYellow , gdVertical);  
  15.  
  16. Tried this, nothing seems to work, all examles result in a black backwall:  (using clYellow as test)
  17.       // bmp.Canvas.FloodFill (bmp.Width, bmp.Height, clYellow, fsSurface  );
  18.       // bmp.Canvas.Brush.Color := clYellow;
  19.       // bmp.Canvas.pen.Color:= clYellow;
  20.  
  21.       img.LoadFromBitmap(bmp.Handle, bmp.MaskHandle);
  22.     finally
  23.       bmp.Free;
  24.     end;
  25.     ADrawer.PutImage(ARect.Left, ARect.Top, img);
  26.     ADoDefaultDrawing := false;
  27.   finally
  28.     img.Free;
  29.   end;
  30. end;  

Win XP, Win7, Win 10 win64 , Lazarus 2.0.6
Delphi/DevExpress

wp

  • Hero Member
  • *****
  • Posts: 6471
Re: TAchart, BeforeCustomDrawBackWall
« Reply #1 on: November 10, 2019, 11:03:46 pm »
If you only need a user-selectable color then you should set the properties Chart.Color and/or Chart.BackColor -- no need for a dedicated event handler.

Anyway, the FloodFill is working, it's just a bit difficult to use. See https://forum.lazarus.freepascal.org/index.php/topic,26963.msg166445.html
Code: Pascal  [Select]
  1. procedure TForm1.Chart1BeforeCustomDrawBackground(ASender: TChart;
  2.   ADrawer: IChartDrawer; const ARect: TRect; var ADoDefaultDrawing: Boolean);
  3. var
  4.   bmp: TBitmap;
  5.   img: TLazIntfImage;
  6. begin
  7.   img := TLazIntfImage.Create(0, 0);
  8.   try
  9.     bmp := TBitmap.Create;
  10.     try
  11.       bmp.SetSize(ARect.Right - ARect.Left, ARect.Bottom - ARect.Top);
  12.       // fill the entire bitmap with clSkyBlue as background color
  13.       bmp.Canvas.Brush.Color := clSkyBlue;
  14.       bmp.Canvas.FillRect(0, 0, bmp.Width, bmp.Height);
  15.       // draw some shape, here: a rectangle
  16.       bmp.Canvas.Rectangle(10, 10, bmp.Width-10, bmp.Height-10);
  17.       bmp.Canvas.Brush.Color := clYellow;  // this sets up the replacement color for the shape's interior.
  18.       bmp.Canvas.FloodFill(bmp.Width div 2, bmp.Height div 2, clSkyBlue, fsSurface);  // clSkyblue is the color to be replaced by the brush color
  19.       img.LoadFromBitmap(bmp.Handle, bmp.MaskHandle);
  20.     finally
  21.       bmp.Free;
  22.     end;
  23.     ADrawer.PutImage(ARect.Left, ARect.Top, img);
  24.     ADoDefaultDrawing := false;
  25.   finally
  26.     img.Free;
  27.   end;
  28. end;

And be aware that the function may not be available on all widgetsets; I only tested on Windows.

Just to fill a rectangle you should call the Canvas method FillRect(x1, y1, x2, y2) (borderless fill with the current Canvas.Brush) or Rectangle(x1, y1, x2, y2) (fill with current Canvas.Brush, border with current Canvas.Pen).

In r62229, I updated the background tutorial code coming with Lazarus in folder components/tachart/tutorials/background to avoid the deprecated events. Thanks for the hint.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

arneolav

  • Full Member
  • ***
  • Posts: 115
    • ElTranslador
Re: TAchart, BeforeCustomDrawBackWall
« Reply #2 on: November 10, 2019, 11:50:46 pm »
Thanks, it works fine and the xtra frame makes it nice, may be i'll keep it as an user option.
....
My idea too was using Chart.Color and/or Chart.BackColor in (old)event BeforeDrawBackWall
to change color "on the fly" but got error SIGDEVG.

This does work ok in event BeforeDrawBackWall:
ACanvas.GradientFill(ARect, color... , color.., gdVertical);
Win XP, Win7, Win 10 win64 , Lazarus 2.0.6
Delphi/DevExpress

wp

  • Hero Member
  • *****
  • Posts: 6471
Re: TAchart, BeforeCustomDrawBackWall
« Reply #3 on: November 11, 2019, 12:25:55 am »
No no! Chart.Color and Chart.BackColor are properties. You only set the values, for example in an OnClick event of a button or a menu item, and the chart is automatically redrawn with the new colors. No need for the custom draw events. Even worse: Setting Color and/or BackColor in these events probably will result in a stack overflow because the events are fired during the painting operation, but changing these properties triggers another painting operation.

Just try the attached demo, and I hope you get the idea.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

arneolav

  • Full Member
  • ***
  • Posts: 115
    • ElTranslador
Re: TAchart, BeforeCustomDrawBackWall
« Reply #4 on: November 11, 2019, 10:30:41 am »
I see ofcorse! THANKS!

I have a setup pgm where all "user stuff" is done, and on return from the setup i found the events BeforeDrawBackWall and BeforeDrawBackground is fired...
....
But I foregot to mention:
if one, by some reason, "have to use the event", one could use the GradientFill with same color  as from/to like this:
bmp.Canvas.GradientFill(Rect(0, 0, bmp.Width, bmp.Height), clSkyBlue, clSkyBlue , gdVertical);
...
May be there should be some derivat of GradientFill, called just "Fill"..

Win XP, Win7, Win 10 win64 , Lazarus 2.0.6
Delphi/DevExpress

wp

  • Hero Member
  • *****
  • Posts: 6471
Re: TAchart, BeforeCustomDrawBackWall
« Reply #5 on: November 14, 2019, 11:18:07 am »
I guess you mean: How to allow the user to select between a uniform, gradient or image background? Instead of many words, I wrote a small demo application - see attachment.

May be there should be some derivat of GradientFill, called just "Fill"..
I don't understand this. Why rename? I could understand that you'd request a dedicated gradient fill method for all the TAChart drawers. The problem with this is that a general gradient method is quite complicated (in addition to the default horizontal/vertical gradients: linear gradients at any angle, circular gradient, conical gradient, controllable center of the circular/conical gradient, several gradient colors etc). It requires a lot of code which the "normal" user of TAChart does not need. Therefore, I think that this is something which should not be inside TAChart to keep it slim (it is already quite fat...).
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10