Difference between revisions of "BGRABitmap tutorial 3"

From Lazarus wiki
Jump to navigationJump to search
m (typos)
m (Text replace - "delphi>" to "syntaxhighlight>")
Line 12: Line 12:
  
 
Add a private variable to the main form to store the image :
 
Add a private variable to the main form to store the image :
<delphi>  TForm1 = class(TForm)
+
<syntaxhighlight>  TForm1 = class(TForm)
 
   private
 
   private
 
     { private declarations }
 
     { private declarations }
Line 18: Line 18:
 
   public
 
   public
 
     { public declarations }
 
     { public declarations }
   end; </delphi>
+
   end; </syntaxhighlight>
  
 
Create the image when the form is created. To do this, double-click on the form, a procedure should appear in the code editor. Add the create instruction :
 
Create the image when the form is created. To do this, double-click on the form, a procedure should appear in the code editor. Add the create instruction :
<delphi>procedure TForm1.FormCreate(Sender: TObject);
+
<syntaxhighlight>procedure TForm1.FormCreate(Sender: TObject);
 
begin
 
begin
 
   image := TBGRABitmap.Create(640,480,BGRAWhite);  //create a 640x480 image
 
   image := TBGRABitmap.Create(640,480,BGRAWhite);  //create a 640x480 image
end; </delphi>
+
end; </syntaxhighlight>
  
 
=== Draw the bitmap ===
 
=== Draw the bitmap ===
  
 
Add an OnPaint handler. To do this, select the main form, then go to the object inspector, in the event tab, and double-click on the OnPaint line. Then, add the drawing code :
 
Add an OnPaint handler. To do this, select the main form, then go to the object inspector, in the event tab, and double-click on the OnPaint line. Then, add the drawing code :
<delphi>procedure TForm1.FormPaint(Sender: TObject);
+
<syntaxhighlight>procedure TForm1.FormPaint(Sender: TObject);
 
begin
 
begin
 
   PaintImage;
 
   PaintImage;
end; </delphi>  
+
end; </syntaxhighlight>  
  
 
Add the PaintImage procedure :
 
Add the PaintImage procedure :
<delphi>procedure TForm1.PaintImage;
+
<syntaxhighlight>procedure TForm1.PaintImage;
 
begin
 
begin
 
   image.Draw(Canvas,0,0,True);
 
   image.Draw(Canvas,0,0,True);
 
end;
 
end;
</delphi>
+
</syntaxhighlight>
  
 
After writing this, put the cursor on PaintImage and press Ctrl-Shift-C to add the declaration to the interface.
 
After writing this, put the cursor on PaintImage and press Ctrl-Shift-C to add the declaration to the interface.
Line 46: Line 46:
  
 
With the object inspector, add handlers for MouseDown and MouseMove events :
 
With the object inspector, add handlers for MouseDown and MouseMove events :
<delphi>procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
+
<syntaxhighlight>procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
 
   Shift: TShiftState; X, Y: Integer);
 
   Shift: TShiftState; X, Y: Integer);
 
begin
 
begin
Line 56: Line 56:
 
begin
 
begin
 
   if ssLeft in Shift then DrawBrush(X,Y);
 
   if ssLeft in Shift then DrawBrush(X,Y);
end;</delphi>
+
end;</syntaxhighlight>
  
 
Add the DrawBrush procedure :
 
Add the DrawBrush procedure :
<delphi>procedure TForm1.DrawBrush(X, Y: Integer);
+
<syntaxhighlight>procedure TForm1.DrawBrush(X, Y: Integer);
 
const radius = 5;
 
const radius = 5;
 
begin
 
begin
Line 67: Line 67:
  
 
   PaintImage;
 
   PaintImage;
end;</delphi>
+
end;</syntaxhighlight>
  
 
After writing this, put the cursor on DrawBrush and press Ctrl-Shift-C to add the declaration to the interface.  
 
After writing this, put the cursor on DrawBrush and press Ctrl-Shift-C to add the declaration to the interface.  
Line 78: Line 78:
 
=== Code ===
 
=== Code ===
  
<delphi>unit UMain;
+
<syntaxhighlight>unit UMain;
  
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
Line 153: Line 153:
 
   {$I UMain.lrs}
 
   {$I UMain.lrs}
  
end.</delphi>
+
end.</syntaxhighlight>
  
 
=== Run the program ===
 
=== Run the program ===
Line 164: Line 164:
  
 
To have a continuous drawing, we need additionnal variables :
 
To have a continuous drawing, we need additionnal variables :
<delphi>  TForm1 = class(TForm)
+
<syntaxhighlight>  TForm1 = class(TForm)
 
     ...
 
     ...
 
   private
 
   private
Line 170: Line 170:
 
     image: TBGRABitmap;
 
     image: TBGRABitmap;
 
     mouseDrawing: boolean;
 
     mouseDrawing: boolean;
     mouseOrigin: TPoint;    </delphi>
+
     mouseOrigin: TPoint;    </syntaxhighlight>
  
 
mouseDrawing will be True during the drawing (with left button pressed) and mouseOrigin will be the starting point of the segment being drawn.
 
mouseDrawing will be True during the drawing (with left button pressed) and mouseOrigin will be the starting point of the segment being drawn.
  
 
The clicking handler becomes a little bit more complicated :
 
The clicking handler becomes a little bit more complicated :
<delphi>procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
+
<syntaxhighlight>procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
 
   Shift: TShiftState; X, Y: Integer);
 
   Shift: TShiftState; X, Y: Integer);
 
begin
 
begin
Line 184: Line 184:
 
     DrawBrush(X,Y,True);
 
     DrawBrush(X,Y,True);
 
   end;
 
   end;
end; </delphi>
+
end; </syntaxhighlight>
 
It initialises the drawing with the current position. Then, it draws as closed segment (note the new parameter for DrawBrush). Indeed, at the beginning, the segment is closed and has a length of zero, which gives a disk :
 
It initialises the drawing with the current position. Then, it draws as closed segment (note the new parameter for DrawBrush). Indeed, at the beginning, the segment is closed and has a length of zero, which gives a disk :
  
Line 194: Line 194:
  
 
That's why we need a new parameter for the DrawBrush function, which becomes :
 
That's why we need a new parameter for the DrawBrush function, which becomes :
<delphi>procedure TForm1.DrawBrush(X, Y: Integer; Closed: Boolean);
+
<syntaxhighlight>procedure TForm1.DrawBrush(X, Y: Integer; Closed: Boolean);
 
const brushRadius = 20;
 
const brushRadius = 20;
 
begin
 
begin
Line 201: Line 201:
  
 
   PaintImage;
 
   PaintImage;
end; </delphi>
+
end; </syntaxhighlight>
  
 
We transmit the parameter Closed to DrawLineAntialias, to indicate whether the segment is closed or not. Note coordinates order. The start position and the end position are swapped. Indeed, for DrawLineAntialias, it's the end that is opened, whereas in this case, we want that the beginning be opened.
 
We transmit the parameter Closed to DrawLineAntialias, to indicate whether the segment is closed or not. Note coordinates order. The start position and the end position are swapped. Indeed, for DrawLineAntialias, it's the end that is opened, whereas in this case, we want that the beginning be opened.
Line 208: Line 208:
  
 
The MouseMove handler becomes :
 
The MouseMove handler becomes :
<delphi>procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
+
<syntaxhighlight>procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
 
   Y: Integer);
 
   Y: Integer);
 
begin
 
begin
 
   if mouseDrawing then DrawBrush(X,Y,False);
 
   if mouseDrawing then DrawBrush(X,Y,False);
end; </delphi>
+
end; </syntaxhighlight>
  
 
Finally, we need to add a MouseUp handler to update mouseDrawing :
 
Finally, we need to add a MouseUp handler to update mouseDrawing :
<delphi>procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
+
<syntaxhighlight>procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
 
   Shift: TShiftState; X, Y: Integer);
 
   Shift: TShiftState; X, Y: Integer);
 
begin
 
begin
 
   if Button = mbLeft then
 
   if Button = mbLeft then
 
     mouseDrawing := False;
 
     mouseDrawing := False;
end;  </delphi>
+
end;  </syntaxhighlight>
  
 
=== Code ===
 
=== Code ===
  
<delphi>unit UMain;
+
<syntaxhighlight>unit UMain;
  
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
Line 314: Line 314:
 
   {$I UMain.lrs}
 
   {$I UMain.lrs}
  
end.</delphi>
+
end.</syntaxhighlight>
  
 
=== Run the program ===
 
=== Run the program ===

Revision as of 14:54, 24 March 2012

Deutsch (de) English (en) español (es) français (fr)


Home | Tutorial 1 | Tutorial 2 | Tutorial 3 | Tutorial 4 | Tutorial 5 | Tutorial 6 | Tutorial 7 | Tutorial 8 | Tutorial 9 | Tutorial 10 | Tutorial 11 | Tutorial 12 | Tutorial 13 | Tutorial 14 | Tutorial 15 | Tutorial 16 | Edit

This tutorial shows you how to draw on a bitmap with the mouse.

Create a new project

Create a new project and add a reference to BGRABitmap, the same way as in the first tutorial.

Create a new image

Add a private variable to the main form to store the image :

  TForm1 = class(TForm)
  private
    { private declarations }
    image: TBGRABitmap;
  public
    { public declarations }
  end;

Create the image when the form is created. To do this, double-click on the form, a procedure should appear in the code editor. Add the create instruction :

procedure TForm1.FormCreate(Sender: TObject);
begin
  image := TBGRABitmap.Create(640,480,BGRAWhite);  //create a 640x480 image
end;

Draw the bitmap

Add an OnPaint handler. To do this, select the main form, then go to the object inspector, in the event tab, and double-click on the OnPaint line. Then, add the drawing code :

procedure TForm1.FormPaint(Sender: TObject);
begin
  PaintImage;
end;

Add the PaintImage procedure :

procedure TForm1.PaintImage;
begin
  image.Draw(Canvas,0,0,True);
end;

After writing this, put the cursor on PaintImage and press Ctrl-Shift-C to add the declaration to the interface.

Handle mouse

With the object inspector, add handlers for MouseDown and MouseMove events :

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then DrawBrush(X,Y);
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if ssLeft in Shift then DrawBrush(X,Y);
end;

Add the DrawBrush procedure :

procedure TForm1.DrawBrush(X, Y: Integer);
const radius = 5;
begin
  image.GradientFill(X-radius,Y-radius, X+radius,Y+radius,
    BGRABlack,BGRAPixelTransparent, gtRadial,
    PointF(X,Y), PointF(X+radius,Y), dmDrawWithTransparency);

  PaintImage;
end;

After writing this, put the cursor on DrawBrush and press Ctrl-Shift-C to add the declaration to the interface.

This procedure draws as radial gradient (gtRadial) :

  • the bounding rectangle is (X-radius,Y-radius, X+radius,Y+radius).
  • the center is black, the border is transparent
  • the center is at (X,Y) and the border at (X+radius,Y)

Code

unit UMain;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  BGRABitmap, BGRABitmapTypes;

type
  { TForm1 }

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure FormPaint(Sender: TObject);
  private
    { private declarations }
    image: TBGRABitmap;
    procedure DrawBrush(X, Y: Integer);
    procedure PaintImage;
  public
    { public declarations }
  end; 

var
  Form1: TForm1; 

implementation

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  image := TBGRABitmap.Create(640,480,BGRAWhite);
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then DrawBrush(X,Y);
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if ssLeft in Shift then DrawBrush(X,Y);
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
  PaintImage;
end;

procedure TForm1.DrawBrush(X, Y: Integer);
const radius = 20;
begin
  image.GradientFill(X-radius,Y-radius, X+radius,Y+radius,
    BGRABlack,BGRAPixelTransparent,gtRadial,
    PointF(X,Y), PointF(X+radius,Y), dmDrawWithTransparency);

  PaintImage;
end;

procedure TForm1.PaintImage;
begin
  image.Draw(Canvas,0,0,True);
end;

initialization
  {$I UMain.lrs}

end.

Run the program

You should be able to draw on the form.

BGRATutorial3.png

Continuous drawing

To have a continuous drawing, we need additionnal variables :

  TForm1 = class(TForm)
    ...
  private
    { private declarations }
    image: TBGRABitmap;
    mouseDrawing: boolean;
    mouseOrigin: TPoint;

mouseDrawing will be True during the drawing (with left button pressed) and mouseOrigin will be the starting point of the segment being drawn.

The clicking handler becomes a little bit more complicated :

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
  begin
    mouseDrawing := True;
    mouseOrigin := Point(X,Y);
    DrawBrush(X,Y,True);
  end;
end;

It initialises the drawing with the current position. Then, it draws as closed segment (note the new parameter for DrawBrush). Indeed, at the beginning, the segment is closed and has a length of zero, which gives a disk :

BGRATutorial3b.png

Little by little, we add a new part, which is an opened segment :

BGRATutorial3c.png

That's why we need a new parameter for the DrawBrush function, which becomes :

procedure TForm1.DrawBrush(X, Y: Integer; Closed: Boolean);
const brushRadius = 20;
begin
  image.DrawLineAntialias(X,Y,mouseOrigin.X,mouseOrigin.Y,BGRA(0,0,0,128),brushRadius,Closed);
  mouseOrigin := Point(X,Y);

  PaintImage;
end;

We transmit the parameter Closed to DrawLineAntialias, to indicate whether the segment is closed or not. Note coordinates order. The start position and the end position are swapped. Indeed, for DrawLineAntialias, it's the end that is opened, whereas in this case, we want that the beginning be opened.

DrawBrush definition must be updated in the interface.

The MouseMove handler becomes :

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if mouseDrawing then DrawBrush(X,Y,False);
end;

Finally, we need to add a MouseUp handler to update mouseDrawing :

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
    mouseDrawing := False;
end;

Code

unit UMain;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  BGRABitmap, BGRABitmapTypes;

type
  { TForm1 }

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormPaint(Sender: TObject);
  private
    { private declarations }
    image: TBGRABitmap;
    mouseDrawing: boolean;
    mouseOrigin: TPoint;
    procedure DrawBrush(X, Y: Integer; Closed: boolean);
    procedure PaintImage;
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  image := TBGRABitmap.Create(640,480,BGRAWhite);
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
  begin
    mouseDrawing := True;
    mouseOrigin := Point(X,Y);
    DrawBrush(X,Y,True);
  end;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  if mouseDrawing then DrawBrush(X,Y,False);
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then
    mouseDrawing := False;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
  PaintImage;
end;

procedure TForm1.DrawBrush(X, Y: Integer; Closed: Boolean);
const brushRadius = 20;
begin
  image.DrawLineAntialias(X,Y,mouseOrigin.X,mouseOrigin.Y,BGRA(0,0,0,128),brushRadius,Closed);
  mouseOrigin := Point(X,Y);

  PaintImage;
end;

procedure TForm1.PaintImage;
begin
  image.Draw(Canvas,0,0,True);
end;

initialization
  {$I UMain.lrs}

end.

Run the program

Now the drawing is almost continous :

BGRATutorial3d.png

Previous tutorial (image loading) | Next tutorial (direct pixel access)