Lazarus

Programming => Graphics and Multimedia => Graphics => Topic started by: Gustavo 'Gus' Carreno on September 17, 2021, 10:17:59 pm

Title: [SOLVED] How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 17, 2021, 10:17:59 pm
Hey Y'all,

I decided to translate this Delphi CAPTCHA Verification (https://github.com/JensBorrisholt/Captcha-Verification) into a Lazarus CAPTCHA verification (https://github.com/gcarreno/lazCAPTCHA), 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][+][-]
  1. procedure TCAPTCHA.RefreshBitmap;
  2. var
  3.   i: Integer;
  4. begin
  5.   FreeAndNil(FCaptchaBitmap);
  6.   FCAPTCHAString:= GenerateCAPTCHAString;
  7.   // Start of Bitmap creation
  8.   FCAPTCHABitmap:= TBitmap.Create;
  9.   FCAPTCHABitmap.Width:= 300;
  10.   FCAPTCHABitmap.Height:= 75;
  11.   FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;
  12.   FCAPTCHABitmap.PixelFormat:= pf24bit;
  13.   // End of Bitmap creation
  14.   for i:= 1 to Length(FCAPTCHAString) do
  15.     DrawLetter(FCAPTCHAString[i], Random(600) + 1, 25 * i - 15);
  16.   DrawLines;
  17. 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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Josh on September 17, 2021, 10:42:47 pm
hi

not at a lazarus install to test,
but maybe setting theformat and color before you set the dimensions would help.

Code: Pascal  [Select][+][-]
  1. // Start of Bitmap creation
  2.   FCAPTCHABitmap:= TBitmap.Create;
  3.   FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;
  4.   FCAPTCHABitmap.PixelFormat:= pf24bit;
  5.   FCAPTCHABitmap.Width:= 300;
  6.   FCAPTCHABitmap.Height:= 75;
  7.  
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: winni on September 17, 2021, 10:49:18 pm
Hi!

Where is DrawLetter from????

It is not from the LCL.

And if you want to change the background:

Code: Pascal  [Select][+][-]
  1. FCAPTCHABitmap.Canvas.Brush.Color := clFuchsia;
  2. FCAPTCHABitmap.Canvas.FillRect(0,0, FCAPTCHABitmap.width,FCAPTCHABitmap.height);
  3.  
  4.  

Winni

Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 17, 2021, 11:00:34 pm
Hey Winni,

Where 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.

And if you want to change the background:
Code: Pascal  [Select][+][-]
  1. FCAPTCHABitmap.Canvas.Brush.Color := clFuchsia;
  2. 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][+][-]
  1. FCAPTCHABitmap.Canvas.FloodFill(0,0, clWhite, fsSurface);
  2. // Also tested
  3. FCAPTCHABitmap.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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 17, 2021, 11:12:14 pm
Hey Josh,

not 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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: winni on September 17, 2021, 11:28:37 pm
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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 17, 2021, 11:41:44 pm
Hey Winni,

Drawing 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.

And 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][+][-]
  1. { Implements CAPTCHA
  2.  
  3. Copyright (c) 2021 Gustavo Carreno <guscarreno@gmail.com>
  4.  
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11.  
  12. The above copyright notice and this permission notice shall be included in all
  13. copies or substantial portions of the Software.
  14.  
  15. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. SOFTWARE.
  22.  
  23. }
  24. unit CAPTCHA;
  25.  
  26. {$mode objfpc}{$H+}
  27.  
  28. interface
  29.  
  30. uses
  31.   Classes
  32. , SysUtils
  33. , LCLType
  34. , LCLIntf
  35. , Graphics
  36. ;
  37.  
  38. type
  39. { TCAPTCHA }
  40.   TCAPTCHA = class(TObject)
  41.   type
  42.     TCharCase = (Lower, Upper, Number);
  43.     TCharCases = set of TCharCase;
  44.  
  45.   const
  46.     cCharCaseAll = [Lower, Upper, Number];
  47.     cCharCaseLetter = [Lower, Upper];
  48.  
  49.   private
  50.     FCAPTCHAString: string;
  51.     FCAPTCHABitmap: TBitmap;
  52.     FCharCase: TCharCases;
  53.  
  54.     function GenerateCAPTCHAString: string;
  55.     procedure DrawLetter(ch: Char; angle, nextPos: Integer);
  56.     procedure DrawLines(aLineCount: Integer = 15);
  57.   protected
  58.   public
  59.     constructor Create(ACharCase: TCharCases = [Upper, Number]);
  60.     destructor Destroy; override;
  61.  
  62.     procedure RefreshBitmap;
  63.     function Validate(const AValue: string;
  64.       ACaseSensetive: Boolean = True): Boolean;
  65.     property Image: TBitmap read FCAPTCHABitmap;
  66.   published
  67.   end;
  68.  
  69. implementation
  70.  
  71. uses
  72.   GraphType
  73. ;
  74.  
  75. { TCAPTCHA }
  76.  
  77. function TCAPTCHA.GenerateCAPTCHAString: string;
  78. const
  79.   NoOfChars = 10;
  80. var
  81.   validChar: String;
  82.   i: Integer;
  83. begin
  84.   Result:= EmptyStr;
  85.   validChar:= EmptyStr;
  86.   if TCharCase.Number in FCharCase then
  87.     validChar:= validChar + '123456789';
  88.  
  89.   if TCharCase.Lower in FCharCase then
  90.     validChar:= validChar + 'abcdefghijklmnopqrstuvwxyz';
  91.  
  92.   if TCharCase.Upper in FCharCase then
  93.     validChar:= validChar + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  94.  
  95.   SetLength(Result, NoOfChars);
  96.  
  97.   for i:= 1 to NoOfChars do
  98.     Result[i]:= validChar[Random(Length(validChar)) + 1];
  99. end;
  100.  
  101. procedure TCAPTCHA.DrawLetter(ch: Char; angle, nextPos: Integer);
  102. var
  103.   logFont: TLogFont;
  104.   fontHandle: THandle;
  105. begin
  106.   logFont.lfheight:= 40;
  107.   logFont.lfwidth:= 20;
  108.   logFont.lfweight:= 900;
  109.  
  110.   logFont.lfEscapement:= angle;
  111.   logFont.lfcharset:= 1;
  112.   logFont.lfoutprecision:= OUT_TT_ONLY_PRECIS;
  113.   logFont.lfquality:= DEFAULT_QUALITY;
  114.   logFont.lfpitchandfamily:= FF_SWISS;
  115.   logFont.lfUnderline:= 0;
  116.   logFont.lfStrikeOut:= 0;
  117.  
  118.   fontHandle:= CreateFontIndirect(logFont);
  119.   SelectObject(FCAPTCHABitmap.Canvas.Handle, fontHandle);
  120.  
  121.   SetTextColor(FCAPTCHABitmap.Canvas.Handle, rgb(0, 180, 0));
  122.   SetBKmode(FCAPTCHABitmap.Canvas.Handle, TRANSPARENT);
  123.  
  124.   SetTextColor(FCAPTCHABitmap.Canvas.Handle, Random(MAXWORD));
  125.   FCAPTCHABitmap.Canvas.TextOut(nextPos, FCAPTCHABitmap.Height div 3, ch);
  126.   DeleteObject(fontHandle);
  127. end;
  128.  
  129. procedure TCAPTCHA.DrawLines(aLineCount: Integer);
  130. var
  131.   i: Integer;
  132. begin
  133.   for i:= 0 to aLineCount do
  134.   begin
  135.     FCAPTCHABitmap.Canvas.Pen.Color:= Random(MAXWORD);
  136.     FCAPTCHABitmap.Canvas.MoveTo(Random(FCAPTCHABitmap.Width), Random(FCAPTCHABitmap.Height));
  137.     FCAPTCHABitmap.Canvas.LineTo(Random(FCAPTCHABitmap.Width), Random(FCAPTCHABitmap.Height));
  138.   end;
  139. end;
  140.  
  141. procedure TCAPTCHA.RefreshBitmap;
  142. var
  143.   i: Integer;
  144. begin
  145.   FreeAndNil(FCaptchaBitmap);
  146.   FCAPTCHAString:= GenerateCAPTCHAString;
  147.   FCAPTCHABitmap:= TBitmap.Create;
  148.   FCAPTCHABitmap.Width:= 300;
  149.   FCAPTCHABitmap.Height:= 75;
  150.   FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;
  151.   FCAPTCHABitmap.PixelFormat:= pf24bit;
  152.   for i:= 1 to Length(FCAPTCHAString) do
  153.     DrawLetter(FCAPTCHAString[i], Random(600) + 1, 25 * i - 15);
  154.   DrawLines;
  155. end;
  156.  
  157. function TCAPTCHA.Validate(const AValue: string;
  158.   ACaseSensetive: Boolean): Boolean;
  159. begin
  160.   if ACaseSensetive then
  161.     Result:= AValue = FCaptchaString
  162.   else
  163.     Result:= SameText(AValue, FCaptchaString);
  164. end;
  165.  
  166. constructor TCAPTCHA.Create(ACharCase: TCharCases);
  167. begin
  168.   FCharCase := ACharCase;
  169.   Randomize;
  170.   RefreshBitmap;
  171. end;
  172.  
  173. destructor TCAPTCHA.Destroy;
  174. begin
  175.   FreeAndNil(FCAPTCHABitmap);
  176.   inherited Destroy;
  177. end;
  178.  
  179. end.
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 18, 2021, 12:13:36 am
Hey Y'all,

FINALLY found the bugger!!!

I forgot to set the PEN colour before performing the FillRect, ARRGGGHHHH!!!!
Code: Pascal  [Select][+][-]
  1. FCAPTCHABitmap.Canvas.Pen.Color:= clWhite;
  2. 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][+][-]
  1. for i:=0 to 75 do
  2. begin
  3.   FCAPTCHABitmap.Canvas.Pen.Color:= clWhite;
  4.   FCAPTCHABitmap.Canvas.MoveTo(0, i);
  5.   FCAPTCHABitmap.Canvas.LineTo(300, i);
  6. 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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: wp on September 18, 2021, 12:15:46 am
Just FillRect the CAPTCHABitmap after setting the Brush.Color:
Code: Pascal  [Select][+][-]
  1. procedure TCAPTCHA.RefreshBitmap;
  2. var
  3.   i: Integer;
  4. begin
  5.   FreeAndNil(FCaptchaBitmap);
  6.   FCAPTCHAString:= GenerateCAPTCHAString;
  7.   FCAPTCHABitmap:= TBitmap.Create;
  8.   FCAPTCHABitmap.Width:= 300;
  9.   FCAPTCHABitmap.Height:= 75;
  10.   FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;
  11. //  FCAPTCHABitmap.PixelFormat:= pf24bit;
  12.   FCAPTCHABitmap.Canvas.FillRect(0, 0, FCAPTCHABitmap.Width, FCAPTCHABitmap.Height);
  13.   for i:= 1 to Length(FCAPTCHAString) do
  14.     DrawLetter(FCAPTCHAString[i], Random(600) + 1, 25 * i - 15);
  15.   DrawLines;
  16. 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][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   FCaptcha := TCaptcha.Create;
  4. end;
  5.  
  6. procedure TForm1.FormDestroy(Sender: TObject);
  7. begin
  8.   FCaptcha.Free;
  9. end;
  10.  
  11. procedure TForm1.FormPaint(Sender: TObject);
  12. begin
  13.   Canvas.Draw(0, 0, FCaptcha.Image);
  14. end;
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: wp on September 18, 2021, 12:19:34 am
I forgot to set the PEN colour before performing the FillRect, ARRGGGHHHH!!!!
Code: Pascal  [Select][+][-]
  1. FCAPTCHABitmap.Canvas.Pen.Color:= clWhite;
  2. FCAPTCHABitmap.Canvas.FillRect(0,0,300,75);
Why Pen.Color? The Canvas.FillRect only needs the Brush.Color
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 18, 2021, 12:26:49 am
Hey WP,

Just FillRect the CAPTCHABitmap after setting the Brush.Color:
Code: Pascal  [Select][+][-]
  1. procedure TCAPTCHA.RefreshBitmap;
  2. var
  3.   i: Integer;
  4. begin
  5.   FreeAndNil(FCaptchaBitmap);
  6.   FCAPTCHAString:= GenerateCAPTCHAString;
  7.   FCAPTCHABitmap:= TBitmap.Create;
  8.   FCAPTCHABitmap.Width:= 300;
  9.   FCAPTCHABitmap.Height:= 75;
  10.   FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;
  11. //  FCAPTCHABitmap.PixelFormat:= pf24bit;
  12.   FCAPTCHABitmap.Canvas.FillRect(0, 0, FCAPTCHABitmap.Width, FCAPTCHABitmap.Height);
  13.   for i:= 1 to Length(FCAPTCHAString) do
  14.     DrawLetter(FCAPTCHAString[i], Random(600) + 1, 25 * i - 15);
  15.   DrawLines;
  16. 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 ;)

I don't know how you paint the captcha. I just put it into the Form's OnPaint, and it works:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   FCaptcha := TCaptcha.Create;
  4. end;
  5.  
  6. procedure TForm1.FormDestroy(Sender: TObject);
  7. begin
  8.   FCaptcha.Free;
  9. end;
  10.  
  11. procedure TForm1.FormPaint(Sender: TObject);
  12. begin
  13.   Canvas.Draw(0, 0, FCaptcha.Image);
  14. 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][+][-]
  1. 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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 18, 2021, 12:28:30 am
Hey WP,

I forgot to set the PEN colour before performing the FillRect, ARRGGGHHHH!!!!
Code: Pascal  [Select][+][-]
  1. FCAPTCHABitmap.Canvas.Pen.Color:= clWhite;
  2. 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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 18, 2021, 12:34:21 am
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:

ARGHHHH, this is really getting in the voodoo area for me, LOL!!!

Cheers,
Gus
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 18, 2021, 12:47:10 am
Hey WP,

If I move the PixelFormat setting above the Brush setting, this now works... WHAT THE ACTUAL FECK!?!?!?

Code: Pascal  [Select][+][-]
  1.   FCAPTCHABitmap:= TBitmap.Create;
  2.   FCAPTCHABitmap.PixelFormat:= pf24bit;
  3.   FCAPTCHABitmap.Width:= cWidth;
  4.   FCAPTCHABitmap.Height:= cHeight;
  5.   FCAPTCHABitmap.Canvas.Brush.Color:= clWhite;
  6.   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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 18, 2021, 01:07:50 am
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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: winni on September 18, 2021, 01:44:18 pm
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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 19, 2021, 08:00:28 am
Hey Winni,

I 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!!!

It 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 (https://forum.lazarus.freepascal.org/index.php/topic,56335.msg418719.html#msg418719) 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
Title: Re: How to create a TBitmap with a white background instead of black?
Post by: Gustavo 'Gus' Carreno on September 19, 2021, 08:04:59 am
Hey Y'all,

I'm gonna mark this thread as SOLVED and create 2 news one to address:

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
TinyPortal © 2005-2018