Recent

Author Topic: Painting on a control - 3 questions: OnPaint, ImageList, program logic  (Read 3386 times)

majolika

  • New Member
  • *
  • Posts: 42
Hello!
I have three newbies question about painting on a control.
For the educational purposes I'm programming a mine sweeper game.

1st question: OnPaint
When I click on a start buttion I show a mine field and fill it with squares. Then I click on the mine field and show a mine. But the mine field fully repaint and my squares is gone. Why?

2nd question: ImageList
ImageList is very usefull control but I found one strange thing: when I'm drawing an same image with ImageList and with Canvas.Draw it gives me different results. Why?

Code: Pascal  [Select][+][-]
  1. ImageList1.Resolution[24].Draw(Bevel1.Canvas,  x, y, 0, True);
Code: Pascal  [Select][+][-]
  1. MineField.Canvas.Draw(x, y, Picture_Loaded_From_TImage_Placed_On_The_Form);

I attached screenshot to show the difference: left, brighter mine field — ImageList, right, darker — Canvas.Draw

3rd question: program logic
First click on the start button works well but the second doesn't work at all. Why?

Here's the minimal proof-of-concept code:

Code: Pascal  [Select][+][-]
  1. unit test_unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls;
  9.  
  10. type
  11.  
  12.   TForm1 = class(TForm)
  13.     Bevel1: TBevel;
  14.     Button1: TButton;
  15.     ImageList1: TImageList;
  16.     procedure Bevel1MouseUp(Sender: TObject; Button: TMouseButton;
  17.       Shift: TShiftState; X, Y: Integer);
  18.     procedure Bevel1Paint(Sender: TObject);
  19.     procedure Button1Click(Sender: TObject);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.   BoxSize: Integer = 24;
  29.   FieldRow: integer = 10;
  30.   FieldCol: integer = 10;
  31.   Button1Clicked: Boolean = False;
  32.   DrawABomb: Boolean = False;
  33.   CoordX, CoordY: Integer;
  34.  
  35. implementation
  36.  
  37. {$R *.lfm}
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. begin
  41.   Button1Clicked := True;
  42.   Bevel1.Width := FieldCol * BoxSize;
  43.   Bevel1.Height := FieldRow * BoxSize;
  44.   Bevel1.Show;
  45. end;
  46.  
  47. procedure TForm1.Bevel1Paint(Sender: TObject);
  48. var
  49.   x, y: Integer;
  50. begin
  51.   if Button1Clicked then begin
  52.      for x := 0 to FieldCol - 1 do begin
  53.          for y := 0 to FieldRow - 1 do begin
  54.              ImageList1.Resolution[24].Draw(Bevel1.Canvas,
  55.              (x * BoxSize), (y * BoxSize), 0, True);
  56.          end;
  57.      end;
  58.      Button1Clicked := False;
  59.   end;
  60.   if DrawABomb then begin
  61.      ImageList1.Resolution[24].Draw(Bevel1.Canvas, CoordX, CoordY, 1, True);
  62.      DrawABomb := False;
  63.   end;
  64. end;
  65.  
  66. procedure TForm1.Bevel1MouseUp(Sender: TObject; Button: TMouseButton;
  67.           Shift: TShiftState; X, Y: Integer);
  68. begin
  69.   if Button = mbLeft then begin
  70.      DrawABomb := True;
  71.      CoordX := X; CoordY := Y;
  72.      Bevel1.Repaint;
  73.   end;
  74. end;
  75.  
  76. end.
  77.  

ASerge

  • Hero Member
  • *****
  • Posts: 2374
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #1 on: January 22, 2025, 05:36:36 pm »
1st question: OnPaint
When I click on a start buttion I show a mine field and fill it with squares. Then I click on the mine field and show a mine. But the mine field fully repaint and my squares is gone. Why?
It is better to use TPaintBox.
In the OnPaint handler, assume that all the content is corrupted and incorrect. Based on this, build the logic of rendering.

majolika

  • New Member
  • *
  • Posts: 42
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #2 on: January 22, 2025, 05:43:26 pm »
It is better to use TPaintBox.
Thanks! I'll try and be back later to say if it works for me.

majolika

  • New Member
  • *
  • Posts: 42
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #3 on: January 22, 2025, 07:23:14 pm »
It is better to use TPaintBox.
No, if I simply replace TBevel with TPaintBox it's not working. Attached pics demonstrates three steps with TBevel (show mine) and PaintBox (show no mine).

Code: Pascal  [Select][+][-]
  1. unit test2_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 = class(TForm)
  13.     Button1: TButton;
  14.     ImageList1: TImageList;
  15.     PaintBox1: TPaintBox;
  16.     procedure Button1Click(Sender: TObject);
  17.     procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  18.               Shift: TShiftState; X, Y: Integer);
  19.     procedure PaintBox1Paint(Sender: TObject);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.   BoxSize: Integer = 24;
  29.   FieldRow: integer = 10;
  30.   FieldCol: integer = 10;
  31.   Button1Clicked: Boolean = False;
  32.   DrawABomb: Boolean = False;
  33.   FieldWidth, FieldHeight: Integer;
  34.  
  35. implementation
  36.  
  37. {$R *.lfm}
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. begin
  41.   Button1Clicked := True;
  42.   PaintBox1.Width := FieldCol * BoxSize;
  43.   PaintBox1.Height := FieldRow * BoxSize;
  44. end;
  45.  
  46. procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  47.           Shift: TShiftState; X, Y: Integer);
  48. begin
  49.   if Button = mbLeft then begin
  50.      DrawABomb := True;
  51.      PaintBox1.Repaint;
  52.   end;
  53. end;
  54.  
  55. procedure TForm1.PaintBox1Paint(Sender: TObject);
  56. var x, y: integer;
  57. begin
  58.   if Button1Clicked then begin
  59.      for x := 0 to FieldCol - 1 do begin
  60.          for y := 0 to FieldRow - 1 do begin
  61.              ImageList1.Resolution[24].Draw(PaintBox1.Canvas,
  62.              (x * BoxSize), (y * BoxSize), 0, True);
  63.          end;
  64.      end;
  65.      Button1Clicked := False;
  66.   end;
  67.   if DrawABomb then begin
  68.      ImageList1.Resolution[24].Draw(PaintBox1.Canvas, x, y, 1, True);
  69.      DrawABomb := False;
  70.   end;
  71. end;
  72.  
  73. end.
  74.  

In the OnPaint handler, assume that all the content is corrupted and incorrect. Based on this, build the logic of rendering.
I found a simplistic clone of MS Paint on Github. It seems that author creates a bitmap in memory, in OnMouseUp/Down/Move draws something on top of this bitmap and then in OnPaint just put the whole bitmap on the PaintBox. But it's kinda wierd solution. Why are the things so complicated?!

ASerge

  • Hero Member
  • *****
  • Posts: 2374
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #4 on: January 22, 2025, 07:55:27 pm »
It seems that author creates a bitmap in memory, in OnMouseUp/Down/Move draws something on top of this bitmap and then in OnPaint just put the whole bitmap on the PaintBox. But it's kinda wierd solution. Why are the things so complicated?!
It just caches a lot of drawings on a separate bitmap. To speed up. But the principle remains the same - redraw each time.

TRon

  • Hero Member
  • *****
  • Posts: 3928
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #5 on: January 22, 2025, 08:18:09 pm »
But it's kinda wierd solution. Why are the things so complicated?!
That is how graphics work.

Think for it a minute. In general why do you see what is on your screen ? How does that compute in practice ? When speaking of raster graphics the graphics card 'draws' the content of a buffer (video memory) to a screen and does that at a certain rate. So each time the graphics in that buffer is completely outputted to the monitor. If you change even a single pixel inside that buffer, the whole buffer outputted to the monitor again. It even outputs that buffer when nothing was changed inside it. Notice the old-school description as in this day and age modern graphic cards work much more complicated. see also howstuffworks..
I do not have to remember anything anymore thanks to total-recall.

majolika

  • New Member
  • *
  • Posts: 42
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #6 on: January 22, 2025, 08:37:19 pm »
It just caches a lot of drawings on a separate bitmap. To speed up. But the principle remains the same - redraw each time.
So I have to do the same, right? Create a bitmap in memory, draw the mine field on this bitmap, place bitmap on PaintBox, trigger OnPaint by Repaint, Update or whatever, draw something (e.g. bomb) on top of mine field, place this new picture on PaintBox, trigger OnPaint again and so on. Not an elegant way. And passing signals like "I press a button" or "I click on mine field" from OnClick to OnPaint with global variables is not an elegant too. Well... I have to deal with it. :)

And I think I realize my ImageList issue. It about transperency. But I have to check it out.

So only one question left: why does start button stops working after first click?

majolika

  • New Member
  • *
  • Posts: 42
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #7 on: January 22, 2025, 08:43:38 pm »
That is how graphics work.
Ok, I get it. But why covering/uncovering my window with another window (e.g. browser) does not triiger repainting and minimizing — trigger? Althouth it's pretty the same for my app form — nothing changed on it.

TRon

  • Hero Member
  • *****
  • Posts: 3928
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #8 on: January 22, 2025, 09:01:07 pm »
Ok, I get it. But why covering/uncovering my window with another window (e.g. browser) does not triiger repainting and minimizing — trigger? Althouth it's pretty the same for my app form — nothing changed on it.
The only difference with other controls is that the painting (refresh) happens automatically on/for certain events.

This can't be done for a graphic control (TPaintbox in this particular case) in a manner that satisfies everyone. The control is simply too basic for that. Though having said that you could perhaps react on another event that signifies to redraw the window or panel at which the paintbox control is positioned and use that to update your paintbox. tbh, I never tried.

btw: your message on how to do it makes it sound very complicated while in fact it is only drawing a bitmap to a canvas of the paintbox and 'catching' the mouse/pixel positions at which was clicked and compared that with your 'cells' location.
« Last Edit: January 22, 2025, 09:04:37 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

majolika

  • New Member
  • *
  • Posts: 42
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #9 on: January 22, 2025, 09:26:27 pm »
you could perhaps react on another event that signifies to redraw the window or panel at which the paintbox control is positioned and use that to update your paintbox. tbh, I never tried.
You wanna say what I can prevent control repainting?
While googling I read something about that but I thought it's needed only when some operations take too much time.

your message on how to do it makes it sound very complicated
No, no, it's not a big deal. :) Dealing with painting, repainting and so on seems to me complicated and unclear.
I almost complited the first draft of my MineSweeper (unchageable 16×16, primitive randomizing the mine field, etc) when I found this repainting problem.

majolika

  • New Member
  • *
  • Posts: 42
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #10 on: January 22, 2025, 09:30:24 pm »
{ offtop on
How do you edit your messages? I dont' see any button or something like that.
offtop off}


TRon

  • Hero Member
  • *****
  • Posts: 3928
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #11 on: January 22, 2025, 09:34:04 pm »
{ offtop on
How do you edit your messages? I dont' see any button or something like that.
offtop off}

As a new user you are not allowed to edit your message until you have done a minimal amount of posts. This is done by the forum software as a protection to prevent trolls and spammers from posting and modifying their message. Unfortunately we have a lot of those.

For now if you need it, quote your original message in a new post and add the modification you wanted to make to make it clear what you wanted to change. Sorry, it is what it is. I am not sure what exactly the amount of posts is btw. I believe it was over 10.
I do not have to remember anything anymore thanks to total-recall.

lainz

  • Hero Member
  • *****
  • Posts: 4685
  • Web, Desktop & Android developer
    • https://lainz.github.io/
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #12 on: January 22, 2025, 09:35:39 pm »
To update less times you can do:

Have a bitmap, update only the parts you changed, repaint is always needed, so no problem with that..

majolika

  • New Member
  • *
  • Posts: 42
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #13 on: January 22, 2025, 09:37:28 pm »
As a new user you are not allowed to edit your message until you have done a minimal amount of posts.
Ahh... Ok, I get it. Thanks for the clarification!

majolika

  • New Member
  • *
  • Posts: 42
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #14 on: January 22, 2025, 09:48:48 pm »
Have a bitmap
MineSweeper is not Doom. There's no need to get as much fps as you can. :)
"Having a bitmap", that's the case! Intermediate layer between one cotrol and another. It's not difficult in realization but it makes program logic more complex, more unclear.
"Beautiful is better than ugly, simple is better than complex...", you know. :)

 

TinyPortal © 2005-2018