Recent

Author Topic: Drawing grid lines on a transparent image  (Read 2692 times)

Tomi

  • Full Member
  • ***
  • Posts: 130
Drawing grid lines on a transparent image
« on: April 24, 2024, 04:15:29 pm »
Hello!

I would like draw some lines for a grid in my program onto a TImage, but my code always shows a black background under the grid.
As I did so far on the based on my modest knowledge and searching on the internet, I draw a TImage on a ScrollBox and make an other TImage on this and a bitmap onto this with the grid lines.
Here is my code:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.gridButtonMouseUp(Sender: TObject; Button: TMouseButton;
  2.   Shift: TShiftState; X, Y: Integer);
  3. var bm: TBitmap;
  4.   hlines,vlines: word;
  5. begin
  6.   if showgrid=true then
  7.   begin
  8.      showgrid:=false;
  9.      if gridon=true then
  10.      begin
  11.        FreeAndNil(gridimage);
  12.        gridon:=false;
  13.      end;
  14.   end
  15.   else
  16.   begin
  17.     showgrid:=true;
  18.     if gridon=false then
  19.     begin
  20.       gridimage:=TImage.Create(terrainScrBox);
  21.       gridimage.Parent:=terrainScrBox;
  22.       gridimage.Left:=0;
  23.       gridimage.Top:=0;
  24.       gridimage.width:=terrainCanvas.width; //terrainCanvas is a TImage component.
  25.       gridimage.height:=terrainCanvas.Height;
  26.       gridimage.Transparent:=true;
  27.       bm:=TBitmap.Create;
  28.       bm.width:=terrainCanvas.width;
  29.       bm.height:=terrainCanvas.Height;
  30.       bm.PixelFormat:=pf32Bit;
  31.       bm.Transparent:=true;
  32.       vlines:=tilewidth;
  33.       hlines:=tileheight;
  34.       bm.canvas.pen.color:=clBlue;
  35.       while vlines<terrainCanvas.width do
  36.       begin
  37.           bm.canvas.line(vlines,0,vlines,terrainCanvas.height);
  38.           inc(vlines,tilewidth);
  39.       end;
  40.       while hlines<terrainCanvas.height do
  41.       begin
  42.           bm.canvas.line(0,hlines,terrainCanvas.width,hlines);
  43.           inc(hlines,tileheight);
  44.       end;
  45.       gridimage.picture.graphic:=bm;
  46.       FreeAndNil(bm);
  47.     end;
  48.     gridon:=true;
  49.   end;
  50. end;

How can I make a transparent image with draw on it to show only its draw onto an other image? Is it possible?

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Drawing grid lines on a transparent image
« Reply #1 on: April 24, 2024, 04:30:06 pm »
When needed, why not draw the lines to original image on the fly?
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Drawing grid lines on a transparent image
« Reply #2 on: April 24, 2024, 05:42:19 pm »
Here an example:
(I have assigned in designer an image to the TImage control.)
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     CheckBox1: TCheckBox;
  16.     Image1: TImage;
  17.     Panel1: TPanel;
  18.     procedure CheckBox1Change(Sender: TObject);
  19.     procedure FormCreate(Sender: TObject);
  20.   strict private
  21.     FBMP: TBitmap;
  22.   private
  23.   public
  24.  
  25.   end;
  26.  
  27. var
  28.   Form1: TForm1;
  29.  
  30. implementation
  31.  
  32. {$R *.lfm}
  33.  
  34. { TForm1 }
  35.  
  36. procedure TForm1.FormCreate(Sender: TObject);
  37. begin
  38.   FBMP := TBitmap.Create;
  39.   FBMP.Assign(Image1.Picture.Bitmap);
  40. end;
  41.  
  42. procedure TForm1.CheckBox1Change(Sender: TObject);
  43. var
  44.   i: Integer;
  45.   GridSize: Integer;
  46.   LBMP: TBitmap;
  47.   X, Y: Integer;
  48. begin
  49.   GridSize := 50;
  50.   LBMP := TBitmap.Create;
  51.   try
  52.     LBMP.PixelFormat := pf32bit;
  53.     LBMP.Width := Image1.ClientRect.Width;
  54.     LBMP.Height := Image1.ClientRect.Height;
  55.     if CheckBox1.Checked then
  56.       begin
  57.         X := 0;
  58.         Y := 0;
  59.         LBMP.Assign(Image1.Picture.Bitmap);
  60.         LBMP.Canvas.Brush.Color := clBlack;
  61.         LBMP.Canvas.Brush.Style := bsSolid;
  62.         for i := 0 to GridSize do
  63.           if ((X + GridSize) < LBMP.Width) then
  64.             begin
  65.               Inc(X, GridSize);
  66.               LBMP.Canvas.FillRect(X, LBMP.Canvas.ClipRect.Top, Succ(X), LBMP.Canvas.ClipRect.Bottom);
  67.             end;
  68.         for i := 0 to GridSize do
  69.           if ((Y + GridSize) < LBMP.Height) then
  70.             begin
  71.               Inc(Y, GridSize);
  72.               LBMP.Canvas.FillRect(LBMP.Canvas.ClipRect.Left, Y, LBMP.Canvas.ClipRect.Right, Succ(Y));
  73.             end;
  74.       end
  75.     else
  76.       LBMP.Assign(FBMP);
  77.     Image1.Picture.Bitmap.Assign(LBMP);
  78.   finally
  79.     LBMP.Free;
  80.   end;
  81. end;
  82.  
  83. end.
If you can follow my code, FBMP keeps the original unmodified version of your image, the checkbox control if there are gridlines drawn over or if it replace current with original.
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Tomi

  • Full Member
  • ***
  • Posts: 130
Re: Drawing grid lines on a transparent image
« Reply #3 on: April 26, 2024, 04:35:49 pm »
Thanks, KodeZwerg; I will try to do it based on your code. I thought exactly like what is in your attached picture.

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Drawing grid lines on a transparent image
« Reply #4 on: April 26, 2024, 06:35:02 pm »
Sure thing!
Ps: In my version I did used a panel, set its color to black, and put a TImage in, since my source image was transparent and somehow the chosen black was not displayed as black. Yeah sometime I need to cheat a little :D
The variable GridSize is to set the number of pixels, if you want two different sizes to get a rectangle instead of a cube just write me :D
If you need a demo project, prepare one in your design and let somehow an option for me to switch grid visible status.
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Tomi

  • Full Member
  • ***
  • Posts: 130
Re: Drawing grid lines on a transparent image
« Reply #5 on: April 27, 2024, 03:04:00 pm »
I understand your example code, KodeZwerg, but what if I would like save the source image into a file without the grid lines?
And why doesn't my modified code show anything?
Code: Pascal  [Select][+][-]
  1. bm:=TBitmap.Create;
  2. bm.PixelFormat:=pf32Bit;
  3. bm.width:=terrainCanvas.width; //terrainCanvas is a TImage.
  4. bm.height:=terrainCanvas.height;
  5. bm.Assign(terrainCanvas.picture.bitmap);
  6. vlines:=tilewidth;
  7. hlines:=tileheight;
  8. bm.canvas.pen.color:=clBlue;
  9. while vlines<terrainCanvas.width do
  10. begin
  11.           bm.canvas.line(vlines,0,vlines,terrainCanvas.height);
  12.           inc(vlines,tilewidth);
  13. end;
  14. while hlines<terrainCanvas.height do
  15. begin
  16.           bm.canvas.line(0,hlines,terrainCanvas.width,hlines);
  17.           inc(hlines,tileheight);
  18. end;
  19. terrainCanvas.Picture.Bitmap.Assign(bm);
  20. FreeAndNil(bm);

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Drawing grid lines on a transparent image
« Reply #6 on: April 27, 2024, 03:14:01 pm »
And why doesn't my modified code show anything?
Because you use different methods that needs different handling.
Code: Pascal  [Select][+][-]
  1.               LBMP.Canvas.FillRect(X, LBMP.Canvas.ClipRect.Top, Succ(X), LBMP.Canvas.ClipRect.Bottom);
Code: Pascal  [Select][+][-]
  1.           bm.canvas.line(vlines,0,vlines,terrainCanvas.height);
As far as I remember, "Line" needs a prior "Move".

//edit
and another problem in your code is, you do refer to TImage within paint methods, thats a no-go, refer only to your bitmap dimensions since the size of a TImage is in many cases not the size of the Image that it hold.
Code: Pascal  [Select][+][-]
  1. bm.width:=terrainCanvas.width; //terrainCanvas is a TImage.
  2. bm.height:=terrainCanvas.height;
both assignments not needed since right after you actual do Assign() what will set the correct dimension
Code: Pascal  [Select][+][-]
  1. while vlines<terrainCanvas.width do
you refer here also wrong, it must be "bm.Width"
and so on :D
« Last Edit: April 27, 2024, 03:30:32 pm by KodeZwerg »
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

wp

  • Hero Member
  • *****
  • Posts: 12457
Re: Drawing grid lines on a transparent image
« Reply #7 on: April 27, 2024, 03:39:35 pm »
The attached demo uses the OnPaint event to draw the grid lines onto the Canvas of the TImage. As a rule of thumb: Try to execute drawing commands only during the paint cycle, ie. in the Paint method or the OnPaint event (or methods called from these). TImage has a buffer bitmap which kind of relaxes this rule, but still I would not swear that it always works as desired if painting occurs outside OnPaint. On other controls, such as TForm, TPanel or TPaintbox, it is a MUST to execute drawing commands only in OnPaint.

My code draws to the Canvas of the TImage. This is NOT the canvas of the bitmap! As a consequence it leaves the original image untouched. So, the order of actions in the Paint cycle is: Draw the original image on the TImage.Canvas, fire the OnPaint event where my code draws the grid (if the checkbox is checked), again on the TImage.Canvas.

Tomi

  • Full Member
  • ***
  • Posts: 130
Re: Drawing grid lines on a transparent image
« Reply #8 on: April 28, 2024, 02:36:17 pm »
I tried make it on the basis of your demo, wp, but this code do nothing:
Code: Pascal  [Select][+][-]
  1. procedure drawgrid(Sender: TObject);
  2. (...)
  3. procedure TForm1.drawgrid(Sender: TObject);
  4. var vlines,hlines: word;
  5. begin
  6.      vlines:=tilewidth;
  7.      hlines:=tileheight;
  8.      gridimage.canvas.pen.color:=clBlue;
  9.      while vlines<terrainCanvas.width do
  10.      begin
  11.           gridimage.canvas.line(vlines,0,vlines,terrainCanvas.height);
  12.           inc(vlines,tilewidth);
  13.      end;
  14.      while hlines<terrainCanvas.height do
  15.      begin
  16.           gridimage.canvas.line(0,hlines,terrainCanvas.width,hlines);
  17.           inc(hlines,tileheight);
  18.      end;
  19. end;
  20. (...)
  21. procedure TForm1.gridonoffbuttonMouseUp(Sender: TObject; Button: TMouseButton;
  22.   Shift: TShiftState; X, Y: Integer);
  23. begin
  24.      gridimage:=TImage.Create(tvScrBox);
  25.      gridimage.Parent:=tvScrBox;
  26.      gridimage.Left:=0;
  27.      gridimage.Top:=0;
  28.      gridimage.width:=terrainCanvas.width;
  29.      gridimage.height:=terrainCanvas.Height;
  30.      //gridimage.Transparent:=true;
  31.      gridimage.OnPaint:=@drawgrid;
  32.      gridimage.Invalidate;
  33. end;

Josh

  • Hero Member
  • *****
  • Posts: 1344
Re: Drawing grid lines on a transparent image
« Reply #9 on: April 28, 2024, 05:16:05 pm »
Project attached that may give you some help.

It allows you to change the gridcolor, grid line styles, and size of grid, if you lock the grid then width and height are synched.

nb An image must be loaded before anything works.

Check the events of the timage and other components
« Last Edit: April 28, 2024, 05:28:47 pm by Josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

wp

  • Hero Member
  • *****
  • Posts: 12457
Re: Drawing grid lines on a transparent image
« Reply #10 on: April 28, 2024, 07:51:18 pm »
I tried make it on the basis of your demo, wp, but this code do nothing:
Yes, but this is different. While I was drawing on the canvas of the image (your "Terraincanvas") you are creating a new image and draw on that one. The problem here is how to create a transparent image by code. Took me some time until I found out, and only by contradicting my previous advice not to draw outside the OnPaint event... TImage is not a simple class, and it is hard to predict what is happening without digging into its implementation code.

A simpler way is to provide the buffer for the grid image manually, by using a TBitmap. The bitmap can be made transparent by filling with a predefined, unused color and setting this as the "transparent color"; after setting the bitmap's Transparent property to true only the pixels having a different color are drawn. This drawing now occurs again in the OnPaint event, but now on the canvas of the base image ("Terraincanvas").

Using this "color transparency", however, is a bit outdated today since it produces sharp transitions between transparent and opaque regions. This is ok for drawing horizontal and vertical lines, but for more general images it is preferrable to use "alpha-channel transparency" for which the bitmap must have 32-bits-per-color, and the additional 8 bits (compared to 24bpp) are used for transparency (or better: opaqueness) values. Unfortunately standard LCL graphics commands cannot use this because TColor reserves that 4th byte for system information. However, there are TLazIntfImage and TLazCanvas in the IntfGraphics unit which do support the alpha channel and can be used to draw on a transparent background. And there are simple commands to convert between TLazIntfImage and TBitmap so that a LazIntfImage can drawn easily on an LCL canvas.

The attached demos show all three techniques.

Tomi

  • Full Member
  • ***
  • Posts: 130
Re: Drawing grid lines on a transparent image
« Reply #11 on: April 29, 2024, 04:32:40 pm »
Wow, thanks for everybody; these are very useful demos for me! But unfortunately that black background still showed under the grid as you can see in my attached program.

wp

  • Hero Member
  • *****
  • Posts: 12457
Re: Drawing grid lines on a transparent image
« Reply #12 on: April 29, 2024, 05:28:19 pm »
It would be helpful if you'd translate your applications to English with English captions and English identifiers. I really don't want to spend my time sending all this to Google translate to find out what this application is supposed to do in detail.

Josh

  • Hero Member
  • *****
  • Posts: 1344
Re: Drawing grid lines on a transparent image
« Reply #13 on: April 29, 2024, 10:09:16 pm »
Just guessing as I cant understand meaning of identifiers,procedures,functions not your fault.

If a grid is required on left image(s) i cannot see a draw for grid.

if you add

Code: Pascal  [Select][+][-]
  1.  
  2. public
  3.   .........
  4.   procedure MyGridPaint(Sender: TObject);
  5.  
  6.  
  7.  
  8. procedure TForm1.MyGridPaint(Sender: TObject);
  9. var x,y:Word;
  10. begin
  11.   if sender is Tcsempek then
  12.   begin
  13.     with sender as Tcsempek do
  14.     begin
  15.       x:=csempeszel;
  16.       y:=csempemag;
  17.       canvas.pen.color:=clBlue;
  18.       while x<width do
  19.       begin
  20.         canvas.line(x,0,x,height);
  21.         inc(x,csempeszel);
  22.       end;
  23.       while y<height do
  24.       begin
  25.         canvas.line(0,y,width,y);
  26.         inc(y,csempemag);
  27.       end;
  28.     end;
  29.   end;
  30. end;            
  31.  
  32.  
  33. then in the create part of button
  34.  
  35.      csempe[forrascsempedb].OnMouseUp:=@fcsegerkatt;
  36.      csempe[forrascsempedb].Transparent:=true;
  37.      csempe[forrascsempedb].OnPaint:=@myGridPaint;  // add this    

I could not test much, was getting access problems and heaptrc issues when compiling in debug mode, so suspect some things are not freed, or your breaking out of something before you tidy up created objects.

thats about as far as I can go. Sorry
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Tomi

  • Full Member
  • ***
  • Posts: 130
Re: Drawing grid lines on a transparent image
« Reply #14 on: May 02, 2024, 03:30:07 pm »
OK and sorry for language confusion. Meanwhile I tried this and that in my code and finally I managed to draw only the grid - but at wrong coordinates.
So, if I commented out that three lines, the grid is visible, but at the left upper coordinate of the screen and besides I can't hide it with button pressing.
If that three lines are not commented, the grid is visible in the right place, but with black background rectangle.
BTW, the terrainCanvas component is a child object of the terrainScrBox, if that matters.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.gridButtonMouseUp(Sender: TObject; Button: TMouseButton;
  2.   Shift: TShiftState; X, Y: Integer);
  3. var bm: TBitmap;
  4.   hlines,vlines: word;
  5. begin
  6.   if showgrid=true then
  7.   begin
  8.      showgrid:=false;
  9.      if gridon=true then
  10.      begin
  11.        FreeAndNil(gridimage);
  12.        gridon:=false;
  13.      end;
  14.   end
  15.   else
  16.   begin
  17.     showgrid:=true;
  18.     if gridon=false then
  19.     begin
  20.       gridimage:=TImage.Create(terrainScrBox);
  21.       gridimage.Parent:=terrainScrBox;
  22.       gridimage.Left:=0;
  23.       gridimage.Top:=0;
  24.       gridimage.width:=terrainCanvas.width; //terrainCanvas is a TImage component.
  25.       gridimage.height:=terrainCanvas.Height;
  26.       griddrawing(gridimage);
  27.     end;
  28.     gridon:=true;
  29.   end;
  30. end;
  31.  
  32. procedure TForm1.griddrawing(Sender: TObject);
  33. var hlines,vlines: word;
  34. begin
  35.      vlines:=tilewidth;
  36.      hlines:=tileheight;
  37.      //with gridimage do // If these lines are commented, the grid is visible without black rectangle,
  38.      //begin // but not at gridimage and I can't switch off it.
  39.        canvas.pen.color:=clBlue;
  40.        while vlines<terrainCanvas.width do
  41.        begin
  42.           canvas.line(vlines,0,vlines,terrainCanvas.height);
  43.           inc(vlines,tilewidth);
  44.        end;
  45.        while hlines<terrainCanvas.height do
  46.        begin
  47.           canvas.line(0,hlines,terrainCanvas.width,hlines);
  48.           inc(hlines,tileheight);
  49.        end;
  50.      //end;
  51. end;

 

TinyPortal © 2005-2018