(Delphi DX 10.3)
I have a big blank (white) canvas (on a Tpanel's descendant) where I draw some text (using Textout(), let's suppose text is always black) and graphics (lines, rectangles, nothing of so complex).
After drawing, I need to change the color of the white background of some specific areas, from white to another colour.
The effect I want to reach is much like an excel sheet with coloured cells. In the attached example, all the columns are created blank (white) like "value" and "difference", then the yellow (price) and the red (result) columns have been coloured.
If I could fill in the areas before writing the text, I would use SetBkMode(TRANSPARENT) and get an optimal result. Unfortunately I need to fill in the areas after writing the text and graphics. The first solution I thought of was pixel-by-pixel replacement (using the Pixels[] function), but it's dramatically slow and graphically unsatisfactory.
So my question is: how can I color the background of an area containing already written text?
Here a Minimal Reproducibile Example.
Button 1 execute pixels substitution, very slow and with unsatisfactory graphical result.
Button 2 fills areas after writing text, using SetBkMode(TRANSPARENT). Perfect result, but I can't do it.
program BK_mode;
uses
Vcl.Forms,
main in 'main.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
////////////////////////////////
unit main;
interface
uses Windows, Forms, SysUtils, Vcl.StdCtrls, UiTypes, System.Classes, Vcl.Controls, Vcl.ExtCtrls, Graphics, Dialogs;
type
TForm1 = class(TForm)
btn_01: TButton;
btn_02: TButton;
procedure btn_02Click(Sender: TObject);
procedure btn_01Click(Sender: TObject);
private
procedure write_text(canvas: TCanvas;x, y : integer;i_fontsize : smallint;const str_text: String;style : TFontStyles;bo_transparent : boolean);
procedure switch_color(canvas : TCanvas;color_source, color_target : TColor);
procedure prepare_example(bo_transparent : boolean);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
BASE_COLOR = clWhite;
ALTERNATIVE_COLOR = clRed;
procedure TForm1.write_text(canvas: TCanvas;x, y : integer;i_fontsize : smallint;const str_text: String;style : TFontStyles;bo_transparent : boolean);
begin
var lo_old_BK_color : TColor := canvas.Brush.Color;
var lo_old_BK_mode := GetBKMode(canvas.Handle);
if bo_transparent then SetBKMode(canvas.Handle, TRANSPARENT)
else begin
SetBKMode(canvas.Handle, OPAQUE);
canvas.Brush.Color := BASE_COLOR
end;
canvas.Font.Color := clBlack;canvas.Font.Size := i_fontsize;canvas.Font.Style := style;
TextOut(canvas.Handle, x, y, PChar(str_text), Length(str_text));
if NOT bo_transparent then canvas.Brush.Color := lo_old_BK_color;
SetBKMode(canvas.Handle, lo_old_BK_mode)
end;
procedure TForm1.switch_color(canvas : TCanvas;color_source, color_target : TColor);
begin
for var x := 0 to clientWidth-1 do
for var y := 0 to clientHeight - 1 do
if (canvas.Pixels[x, y] = color_source) then canvas.Pixels[x, y] := color_target
end;
procedure TForm1.prepare_example(bo_transparent : boolean);
begin
if bo_transparent then Color := ALTERNATIVE_COLOR else Color := BASE_COLOR;
invalidate;
application.MessageBox('Click me', 'Test');
canvas.Rectangle(10, 10, 200, 100);
canvas.MoveTo(10, 110);canvas.LineTo(200, 140);
canvas.MoveTo(10, 140);canvas.LineTo(200, 110);
write_text(canvas, 30, 30, 14, 'This is a text!', [], bo_transparent);
write_text(canvas, 30, 60, 11, 'This is another text!', [fsBold, fsItalic], bo_transparent)
end;
procedure TForm1.btn_01Click(Sender: TObject);
begin
prepare_example(FALSE);
switch_color(canvas, BASE_COLOR, ALTERNATIVE_COLOR)
end;
procedure TForm1.btn_02Click(Sender: TObject);
begin
prepare_example(TRUE)
end;
end.
/////////////////////////////////////////////////////////
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 283
ClientWidth = 208
Color = clWhite
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
DesignSize = (
208
283)
PixelsPerInch = 96
TextHeight = 13
object btn_01: TButton
Left = 17
Top = 161
Width = 178
Height = 51
Anchors = [akLeft, akRight, akBottom]
Caption = '1. write text and fill area'
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Arial'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 0
WordWrap = True
OnClick = btn_01Click
end
object btn_02: TButton
Left = 17
Top = 220
Width = 178
Height = 51
Anchors = [akLeft, akRight, akBottom]
Caption = '2. fill area then write text'
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Arial'
Font.Style = [fsBold]
ParentFont = False
TabOrder = 1
WordWrap = True
OnClick = btn_02Click
end
end