[SOLVED] How to create a TBitmap with a white background instead of black? lazarus / 39-Graphics ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 20:17 Hey Y'all, I decided to translate this Delphi CAPTCHA Verification into a Lazarus CAPTCHA verification, but as you can attest from the screenshots provided on both repositories, my version has the image with a black background. How can I make the TBitmap have a clWhite background when creating it? Here's the piece of code that crates the TBitmap: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCAPTCHA.RefreshBitmap;var  i: Integer;begin  FreeAndNil(FCaptchaBitmap);  FCAPTCHAString:= GenerateCAPTCHAString;  // Start of Bitmap creation  FCAPTCHABitmap:= TBitmap.Create;  FCAPTCHABitmap.Width:= 300;  FCAPTCHABitmap.Height:= 75;  FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;  FCAPTCHABitmap.PixelFormat:= pf24bit;  // End of Bitmap creation  for i:= 1 to Length(FCAPTCHAString) do    DrawLetter(FCAPTCHAString[i], Random(600) + 1, 25 * i - 15);  DrawLines;end; I've copied this from the original Delphi version and on their screenshot the background is white but not in my case. Is this a Windows versus Linux thing? Is it a Lazarus version issue? I welcome any help on this, Y'all!! Cheers, Gus ---------------------------------------------------------------------- Josh · 2021-09-17 20:42 hi not at a lazarus install to test, but maybe setting theformat and color before you set the dimensions would help. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // Start of Bitmap creation  FCAPTCHABitmap:= TBitmap.Create;  FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;  FCAPTCHABitmap.PixelFormat:= pf24bit;  FCAPTCHABitmap.Width:= 300;  FCAPTCHABitmap.Height:= 75; ---------------------------------------------------------------------- winni · 2021-09-17 20:49 Hi! Where is DrawLetter from???? It is not from the LCL. And if you want to change the background: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FCAPTCHABitmap.Canvas.Brush.Color := clFuchsia;FCAPTCHABitmap.Canvas.FillRect(0,0, FCAPTCHABitmap.width,FCAPTCHABitmap.height);   Winni ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 21:00 Hey Winni, Quote from: winni on September 17, 2021, 10:49:18 pmWhere is DrawLetter from???? It is not from the LCL. It comes from another member of the TCAPTCHA object that draws one letter at a time, and no not the LCL. Quote from: winni on September 17, 2021, 10:49:18 pmAnd if you want to change the background: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FCAPTCHABitmap.Canvas.Brush.Color := clFuchsia;FCAPTCHABitmap.Canvas.FillRect(0,0, FCAPTCHABitmap.width,FCAPTCHABitmap.height); Looking at your code it makes sense that it should work, but upon testing it, I still have the black background. Could you please clone the repo and have a go at it, cuz I'm now a bit baffled why it doesn't work. My earlier attempt was with FloodFill(also with no avail): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FCAPTCHABitmap.Canvas.FloodFill(0,0, clWhite, fsSurface);// Also testedFCAPTCHABitmap.Canvas.FloodFill(0,0, clWhite, fsBorder); I've also tested on Lazarus Laz-main(former trunk) and Lazarus 2.0.12, also to no avail Cheers, Gus ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 21:12 Hey Josh, Quote from: josh on September 17, 2021, 10:42:47 pmnot at a lazarus install to test, but maybe setting theformat and color before you set the dimensions would help. Yeah, your way of thinking makes sense and I even thought about that when I started. I tested it then, but just to make sure I tested again and... nope, still have a black background Nonetheless, many thanks for another point of view!! When you're at a Lazarus install, would be wiling to clone the repo and have a go? Many thanks if you will!! I'm getting a bit desperate now and will accept any suggestion, even knocking on wood and walking counter-clock wise around the room once, or sum such silliness, if that would help, LOL!! Cheers, Gus ---------------------------------------------------------------------- winni · 2021-09-17 21:28 Hi! Drawing on a canvas must be done in the onPaint event of a component. Outside of onPaint it fails on some OS. And show the Header of the procedure DrawLetter. Perhaps we can do a workaround. Winni ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 21:41 Hey Winni, Quote from: winni on September 17, 2021, 11:28:37 pmDrawing on a canvas must be done in the onPaint event of a component. Outside of onPaint it fails on some OS. That's interesting since the TBitmap is not being shown, it's just a Field of the object. But you do pose an interesting point... Still not quite sure what to do with it, but it's a rather interesting point. Quote from: winni on September 17, 2021, 11:28:37 pmAnd show the Header of the procedure DrawLetter. Perhaps we can do a workaround. I'm attaching the project bellow and here's the complete code for the TCAPTCHA object: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i { Implements CAPTCHA Copyright (c) 2021 Gustavo Carreno  Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE. }unit CAPTCHA; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, LCLType, LCLIntf, Graphics; type{ TCAPTCHA }  TCAPTCHA = class(TObject)  type    TCharCase = (Lower, Upper, Number);    TCharCases = set of TCharCase;   const    cCharCaseAll = [Lower, Upper, Number];    cCharCaseLetter = [Lower, Upper];   private    FCAPTCHAString: string;    FCAPTCHABitmap: TBitmap;    FCharCase: TCharCases;     function GenerateCAPTCHAString: string;    procedure DrawLetter(ch: Char; angle, nextPos: Integer);    procedure DrawLines(aLineCount: Integer = 15);  protected  public    constructor Create(ACharCase: TCharCases = [Upper, Number]);    destructor Destroy; override;     procedure RefreshBitmap;    function Validate(const AValue: string;    ACaseSensetive: Boolean = True): Boolean;    property Image: TBitmap read FCAPTCHABitmap;  published  end; implementation uses  GraphType; { TCAPTCHA } function TCAPTCHA.GenerateCAPTCHAString: string;const  NoOfChars = 10;var  validChar: String;  i: Integer;begin  Result:= EmptyStr;  validChar:= EmptyStr;  if TCharCase.Number in FCharCase then    validChar:= validChar + '123456789';   if TCharCase.Lower in FCharCase then    validChar:= validChar + 'abcdefghijklmnopqrstuvwxyz';   if TCharCase.Upper in FCharCase then    validChar:= validChar + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';   SetLength(Result, NoOfChars);   for i:= 1 to NoOfChars do    Result[i]:= validChar[Random(Length(validChar)) + 1];end; procedure TCAPTCHA.DrawLetter(ch: Char; angle, nextPos: Integer);var  logFont: TLogFont;  fontHandle: THandle;begin  logFont.lfheight:= 40;  logFont.lfwidth:= 20;  logFont.lfweight:= 900;   logFont.lfEscapement:= angle;  logFont.lfcharset:= 1;  logFont.lfoutprecision:= OUT_TT_ONLY_PRECIS;  logFont.lfquality:= DEFAULT_QUALITY;  logFont.lfpitchandfamily:= FF_SWISS;  logFont.lfUnderline:= 0;  logFont.lfStrikeOut:= 0;   fontHandle:= CreateFontIndirect(logFont);  SelectObject(FCAPTCHABitmap.Canvas.Handle, fontHandle);   SetTextColor(FCAPTCHABitmap.Canvas.Handle, rgb(0, 180, 0));  SetBKmode(FCAPTCHABitmap.Canvas.Handle, TRANSPARENT);   SetTextColor(FCAPTCHABitmap.Canvas.Handle, Random(MAXWORD));  FCAPTCHABitmap.Canvas.TextOut(nextPos, FCAPTCHABitmap.Height div 3, ch);  DeleteObject(fontHandle);end; procedure TCAPTCHA.DrawLines(aLineCount: Integer);var  i: Integer;begin  for i:= 0 to aLineCount do  begin    FCAPTCHABitmap.Canvas.Pen.Color:= Random(MAXWORD);    FCAPTCHABitmap.Canvas.MoveTo(Random(FCAPTCHABitmap.Width), Random(FCAPTCHABitmap.Height));    FCAPTCHABitmap.Canvas.LineTo(Random(FCAPTCHABitmap.Width), Random(FCAPTCHABitmap.Height));  end;end; procedure TCAPTCHA.RefreshBitmap;var  i: Integer;begin  FreeAndNil(FCaptchaBitmap);  FCAPTCHAString:= GenerateCAPTCHAString;  FCAPTCHABitmap:= TBitmap.Create;  FCAPTCHABitmap.Width:= 300;  FCAPTCHABitmap.Height:= 75;  FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;  FCAPTCHABitmap.PixelFormat:= pf24bit;  for i:= 1 to Length(FCAPTCHAString) do    DrawLetter(FCAPTCHAString[i], Random(600) + 1, 25 * i - 15);  DrawLines;end; function TCAPTCHA.Validate(const AValue: string;  ACaseSensetive: Boolean): Boolean;begin  if ACaseSensetive then    Result:= AValue = FCaptchaString  else    Result:= SameText(AValue, FCaptchaString);end; constructor TCAPTCHA.Create(ACharCase: TCharCases);begin  FCharCase := ACharCase;  Randomize;  RefreshBitmap;end; destructor TCAPTCHA.Destroy;begin  FreeAndNil(FCAPTCHABitmap);  inherited Destroy;end; end. ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 22:13 Hey Y'all, FINALLY found the bugger!!! I forgot to set the PEN colour before performing the FillRect, ARRGGGHHHH!!!! Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FCAPTCHABitmap.Canvas.Pen.Color:= clWhite;FCAPTCHABitmap.Canvas.FillRect(0,0,300,75); I was quite intrigued that the BLOODY lines where being drawn, but not the FRIGGIN FillRect so I kinda brute force it: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i for i:=0 to 75 dobegin  FCAPTCHABitmap.Canvas.Pen.Color:= clWhite;  FCAPTCHABitmap.Canvas.MoveTo(0, i);  FCAPTCHABitmap.Canvas.LineTo(300, i);end; And it worked!!! So, on a DUH!! moment I noticed that I change the Brush, but not the Pen!!! Once I fixed that, all is good!!!! @Winni: Your suggestion was spot on!! We both dismissed the BLOODY Pen change assuming that the Brush would do it LOL!!! Attached is version v0.2 with the corrected code, FINALLY!!!!! I'm such a dumb-dumb LOL!!!  Cheers, Gus ---------------------------------------------------------------------- wp · 2021-09-17 22:15 Just FillRect the CAPTCHABitmap after setting the Brush.Color: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCAPTCHA.RefreshBitmap;var  i: Integer;begin  FreeAndNil(FCaptchaBitmap);  FCAPTCHAString:= GenerateCAPTCHAString;  FCAPTCHABitmap:= TBitmap.Create;  FCAPTCHABitmap.Width:= 300;  FCAPTCHABitmap.Height:= 75;  FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;//  FCAPTCHABitmap.PixelFormat:= pf24bit;  FCAPTCHABitmap.Canvas.FillRect(0, 0, FCAPTCHABitmap.Width, FCAPTCHABitmap.Height);  for i:= 1 to Length(FCAPTCHAString) do    DrawLetter(FCAPTCHAString[i], Random(600) + 1, 25 * i - 15);  DrawLines;end; I don't know how you paint the captcha. I just put it into the Form's OnPaint, and it works: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.FormCreate(Sender: TObject);begin  FCaptcha := TCaptcha.Create;end; procedure TForm1.FormDestroy(Sender: TObject);begin  FCaptcha.Free;end; procedure TForm1.FormPaint(Sender: TObject);begin  Canvas.Draw(0, 0, FCaptcha.Image);end; ---------------------------------------------------------------------- wp · 2021-09-17 22:19 Quote from: Gustavo 'Gus' Carreno on September 18, 2021, 12:13:36 amI forgot to set the PEN colour before performing the FillRect, ARRGGGHHHH!!!! Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FCAPTCHABitmap.Canvas.Pen.Color:= clWhite;FCAPTCHABitmap.Canvas.FillRect(0,0,300,75);Why Pen.Color? The Canvas.FillRect only needs the Brush.Color ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 22:26 Hey WP, Quote from: wp on September 18, 2021, 12:15:46 amJust FillRect the CAPTCHABitmap after setting the Brush.Color: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TCAPTCHA.RefreshBitmap;var  i: Integer;begin  FreeAndNil(FCaptchaBitmap);  FCAPTCHAString:= GenerateCAPTCHAString;  FCAPTCHABitmap:= TBitmap.Create;  FCAPTCHABitmap.Width:= 300;  FCAPTCHABitmap.Height:= 75;  FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;//  FCAPTCHABitmap.PixelFormat:= pf24bit;  FCAPTCHABitmap.Canvas.FillRect(0, 0, FCAPTCHABitmap.Width, FCAPTCHABitmap.Height);  for i:= 1 to Length(FCAPTCHAString) do    DrawLetter(FCAPTCHAString[i], Random(600) + 1, 25 * i - 15);  DrawLines;end; I'll try your approach, but it looks like with the PixelFormat set to pf24bit, or after setting that, I need to change the Pen ¯\_(ツ)_/¯ I'm in no matter any expert in the Bitmap department and this is all voodoo to me Quote from: wp on September 18, 2021, 12:15:46 amI don't know how you paint the captcha. I just put it into the Form's OnPaint, and it works: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.FormCreate(Sender: TObject);begin  FCaptcha := TCaptcha.Create;end; procedure TForm1.FormDestroy(Sender: TObject);begin  FCaptcha.Free;end; procedure TForm1.FormPaint(Sender: TObject);begin  Canvas.Draw(0, 0, FCaptcha.Image);end; I don't paint the Paint the Captcha, I do this to transfer the TCAPTCHA Image to the TImage on the form: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i imgCAPTCHA.Picture.Assign(FCAPTCHA.Image); And it works all the time I do it. So in conclusion, what the FECK does the PixelFomat:= pf24bit have that messes up with the Brush/Pen thingamaboob? Cheers, Gus ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 22:28 Hey WP, Quote from: wp on September 18, 2021, 12:19:34 amQuote from: Gustavo 'Gus' Carreno on September 18, 2021, 12:13:36 amI forgot to set the PEN colour before performing the FillRect, ARRGGGHHHH!!!! Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i FCAPTCHABitmap.Canvas.Pen.Color:= clWhite;FCAPTCHABitmap.Canvas.FillRect(0,0,300,75);Why Pen.Color? The Canvas.FillRect only needs the Brush.Color I believe in you mate, but the compiled app disagrees with you!! Sorry Cheers, Gus ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 22:34 Hey Y'all, Crap, CRAP and double CRAP, know I have a difference in behaviour by just changing from Debug to Release!!! Have a look at the attached images!!! As you can see, on the release image, the characters are NOT: ScaledRotatedSame font? ARGHHHH, this is really getting in the voodoo area for me, LOL!!! Cheers, Gus ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 22:47 Hey WP, If I move the PixelFormat setting above the Brush setting, this now works... WHAT THE ACTUAL FECK!?!?!? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   FCAPTCHABitmap:= TBitmap.Create;  FCAPTCHABitmap.PixelFormat:= pf24bit;  FCAPTCHABitmap.Width:= cWidth;  FCAPTCHABitmap.Height:= cHeight;  FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;  FCAPTCHABitmap.Canvas.FillRect(0,0,cWidth,cHeight); Why is the PixelFormat messing so much with the Pen/Brush thingamaboob!?!?!? Does this need to be reported as a bug?!?! And what gives with the Release Build Mode messing with the TLogFont?!?!?!? Cheers, Gus ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-17 23:07 Hey Y'all, Ok, got a bit further: If on Release I drop down the Optimization to -O1 the fonts work great; Anything above that messes completely with the fonts in the way I've shown on the pictures. So what gives!?!?!?! Another thing to report!?!?! I still need to update my trunk install to Laz-main and then test it there to see if the same happens. I've been waiting for @Don to stabilize fpcupdeluxe on the GitLab front to catch up with the default branch... But since I'm still on a SVN repo, and I'm not sure @Don got the repo switch done well, I'm a bit skiditsh Cheers, Gus ---------------------------------------------------------------------- winni · 2021-09-18 11:44 Hi Gus! I made you a little demp project that covers the graphic part of the Captcha project. It uses BGRAbitmap to show how much easier are graphics solved than with a TBitmap. The BGRAbitmapPack can be installed via the Online Package Manager. Screenshot an project attached. Winni ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-19 06:00 Hey Winni, Quote from: winni on September 18, 2021, 01:44:18 pmI made you a little demo project that covers the graphic part of the Captcha project. You are the kindest person, THANK YOU so VERY much!!! I promise I'll have a good look at it!! I'll for sure learn something new and I'm always grateful for that!! My first impression is that the font size and placement is too easy to discern. But that should be easy to refine Nonetheless, great effort for which I'm very grateful!!! Quote from: winni on September 18, 2021, 01:44:18 pmIt uses BGRAbitmap to show how much easier are graphics solved than with a TBitmap. I would agree with you on using an easier image manipulation library, but I think that Leledumbo's post kinda made me realise that using just what FPC/Lazarus provides by default, would be a better pattern. Having no external dependencies in the case of put it on OPM is a good argument, wouldn't you agree? Cheers, Gus ---------------------------------------------------------------------- Gustavo 'Gus' Carreno · 2021-09-19 06:04 Hey Y'all, I'm gonna mark this thread as SOLVED and create 2 news one to address: Why setting TBitmap's PixelFormat resets the Brush/PenWhy having a compile Optimization bigger than 1 messes with TLogFont's font size and rotation For all that answered on this one I'm eternally grateful since I got my solution, even if it did indeed spawn another issue, LOL!! Cheers, Gus