hush021299
asked on
Bitmap Editing with paint box
I try to ad a basic bmp (or Icon) editing feature into my app. So I am testing around with a paintbox.
I plan to load an image, convert to bmp, edit it in the paint box, convert back and save.
I guess, this will be a way, but it takes a while. Especially since I want to draw "blockwise" (zooming into the bmp and simulating big pixels). Maybe I should use a DrawGrid instead?
Beside this, there are many issues need to be resolved like drawing this frames while moving the mouse, undo (should I save old bmps etc, or drawing vectors like described in mastering delphi?);
Hence I look forward to acomplish this. I want to know whether this is the right approach before dwelling deep into this stuff. Maybe sombody has finished code for such a thing.
I would give more points for a finalized solution. The 125 points are meant for input which really helps me further.
I have created my first simple example on this topic:
(* Unit ========================== ======*)
unit BMPEditU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
PaintBox1: TPaintBox;
Button1: TButton;
Panel1: TPanel;
CD: TColorDialog;
rgZoom: TRadioGroup;
RGTool: TRadioGroup;
procedure Button1Click(Sender: TObject);
procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Panel1Click(Sender: TObject);
procedure rgZoomClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
Zoom:integer;
XD,YD,XU,YU:Integer;
BM:TBitmap;
procedure DrawPixes(x, y: integer; col: TColor);
procedure DrawLine(xS, YS, xE, yE, size: integer; Col: TColor);
procedure DrawCircle(xS,YS,xE,yE,siz e:integer; Col:TColor);
procedure DrawBox(xS, YS, xE, yE, size: integer; Col: TColor);
procedure Flood(xS, YS: integer; Col: TColor);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender : TObject);
begin
BM.LoadFromFile('D:\My Documents\My Programs\Glyphs\ACCOUNT.BM P');
PaintBox1.canvas.Draw(0,0, bm);
end;
procedure TForm1.PaintBox1MouseDown( Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
XD:=X;YD:=Y;
end;
procedure TForm1.DrawPixes(x,y:integ er;col:TCo lor);
var i,j,x1,y1:integer;
begin
if (zoom > 0) AND (BM<>nil) then
begin
x1:=x div zoom * zoom;
y1:=y div zoom * zoom;
for i:= 1 to zoom do
for j:= 1 to zoom do
paintbox1.Canvas.Pixels[x1 +i,y1+j]:= col;
end;
end;
procedure TForm1.Panel1Click(Sender: TObject);
begin
cd.execute;
panel1.Color:=cd.Color;
end;
procedure TForm1.rgZoomClick(Sender: TObject);
var w,h:integer;
begin
w:=bm.Width;
h:=BM.Height;
Paintbox1.Width:=w*16;
Paintbox1.Height:=h*16;
case RGZoom.ItemIndex of
0: begin Zoom:=1; w:=bm.Width; h:=BM.Height; end;
1: begin Zoom:=4; w:=bm.Width*4; h:=BM.Height*4; end;
2: begin Zoom:=16; w:=bm.Width*16; h:=BM.Height*16; end;
end;
Paintbox1.Refresh;
PaintBox1.canvas.stretchDr aw(Rect(0, 0,w,h),bm) ;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
BM:=TBitmap.create;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
bm.free;
end;
procedure TForm1.PaintBox1MouseMove( Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if (ssLeft in shift) AND (RGTool.itemindex=0)then
DrawPixes(x,y,panel1.color );
end;
procedure TForm1.PaintBox1MouseUp(Se nder: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
XU:=X;YU:=Y;
case RGTool.itemindex of
0: if (XU = XD) and (YU=YD) then DrawPixes(x,y,panel1.color );
1: DrawLine(xd,yd,xu,yu,zoom, Panel1.color);
2: DrawCircle(xd,yd,xu,yu,zoo m, Panel1.color);
3: DrawBox(xd,yd,xu,yu,zoom, Panel1.color);
4: Flood(xd,yd, Panel1.color);
end;
end;
procedure TForm1.DrawLine(xS,YS,xE,y E,size:int eger; Col:TColor);
begin
paintbox1.Canvas.MoveTo(xS ,yd);
paintbox1.Canvas.pen.Color :=col;
paintbox1.Canvas.pen.Width :=size;
paintbox1.Canvas.LineTo(xE ,yE);
end;
procedure TForm1.DrawCircle(xS,YS,xE ,yE,size:i nteger; Col:TColor);
begin
paintbox1.Canvas.MoveTo(xS ,yd);
paintbox1.Canvas.pen.Color :=col;
paintbox1.Canvas.pen.Width :=size;
paintbox1.Canvas.brush.Sty le :=bsClear;
paintbox1.Canvas.Ellipse(x s,ys,xe,ye );
end;
procedure TForm1.DrawBox(xS,YS,xE,yE ,size:inte ger; Col:TColor);
begin
paintbox1.Canvas.MoveTo(xS ,yd);
paintbox1.Canvas.pen.Color :=col;
paintbox1.Canvas.pen.Width :=size;
paintbox1.Canvas.brush.Sty le :=bsClear;
paintbox1.Canvas.Rectangle (xs,ys,xe, ye);
end;
procedure TForm1.Flood(xS,YS :integer; Col:TColor);
var c:TColor;
begin
paintbox1.Canvas.brush.Col or:=Col;
c:=paintbox1.Canvas.Pixels [xs,ys];
paintbox1.Canvas.FloodFill (xs,ys,c,f sSurface);
end;
end.
(*
========================== ========== =======
The form:
========================== ========== =======
*)
object Form1: TForm1
Left = 233
Top = 107
Width = 696
Height = 480
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Shell Dlg 2'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 13
object PaintBox1: TPaintBox
Left = 32
Top = 24
Width = 321
Height = 329
OnMouseDown = PaintBox1MouseDown
OnMouseMove = PaintBox1MouseMove
OnMouseUp = PaintBox1MouseUp
end
object Button1: TButton
Left = 32
Top = 368
Width = 75
Height = 25
Caption = 'Load'
TabOrder = 0
OnClick = Button1Click
end
object Panel1: TPanel
Left = 32
Top = 400
Width = 73
Height = 25
Caption = 'Color'
TabOrder = 1
OnClick = Panel1Click
end
object rgZoom: TRadioGroup
Left = 128
Top = 360
Width = 89
Height = 65
Caption = 'Zoom'
ItemIndex = 0
Items.Strings = (
'1'
'4'
'16')
TabOrder = 2
OnClick = rgZoomClick
end
object RGTool: TRadioGroup
Left = 248
Top = 328
Width = 89
Height = 113
Caption = 'RGTool'
ItemIndex = 0
Items.Strings = (
'Free line'
'Line'
'Circle'
'Rectangle'
'Flodfill')
TabOrder = 3
end
object CD: TColorDialog
Ctl3D = True
Left = 16
Top = 400
end
end
I plan to load an image, convert to bmp, edit it in the paint box, convert back and save.
I guess, this will be a way, but it takes a while. Especially since I want to draw "blockwise" (zooming into the bmp and simulating big pixels). Maybe I should use a DrawGrid instead?
Beside this, there are many issues need to be resolved like drawing this frames while moving the mouse, undo (should I save old bmps etc, or drawing vectors like described in mastering delphi?);
Hence I look forward to acomplish this. I want to know whether this is the right approach before dwelling deep into this stuff. Maybe sombody has finished code for such a thing.
I would give more points for a finalized solution. The 125 points are meant for input which really helps me further.
I have created my first simple example on this topic:
(* Unit ==========================
unit BMPEditU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
PaintBox1: TPaintBox;
Button1: TButton;
Panel1: TPanel;
CD: TColorDialog;
rgZoom: TRadioGroup;
RGTool: TRadioGroup;
procedure Button1Click(Sender: TObject);
procedure PaintBox1MouseDown(Sender:
Shift: TShiftState; X, Y: Integer);
procedure Panel1Click(Sender: TObject);
procedure rgZoomClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure PaintBox1MouseMove(Sender:
Y: Integer);
procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
Zoom:integer;
XD,YD,XU,YU:Integer;
BM:TBitmap;
procedure DrawPixes(x, y: integer; col: TColor);
procedure DrawLine(xS, YS, xE, yE, size: integer; Col: TColor);
procedure DrawCircle(xS,YS,xE,yE,siz
procedure DrawBox(xS, YS, xE, yE, size: integer; Col: TColor);
procedure Flood(xS, YS: integer; Col: TColor);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender
begin
BM.LoadFromFile('D:\My Documents\My Programs\Glyphs\ACCOUNT.BM
PaintBox1.canvas.Draw(0,0,
end;
procedure TForm1.PaintBox1MouseDown(
Shift: TShiftState; X, Y: Integer);
begin
XD:=X;YD:=Y;
end;
procedure TForm1.DrawPixes(x,y:integ
var i,j,x1,y1:integer;
begin
if (zoom > 0) AND (BM<>nil) then
begin
x1:=x div zoom * zoom;
y1:=y div zoom * zoom;
for i:= 1 to zoom do
for j:= 1 to zoom do
paintbox1.Canvas.Pixels[x1
end;
end;
procedure TForm1.Panel1Click(Sender:
begin
cd.execute;
panel1.Color:=cd.Color;
end;
procedure TForm1.rgZoomClick(Sender:
var w,h:integer;
begin
w:=bm.Width;
h:=BM.Height;
Paintbox1.Width:=w*16;
Paintbox1.Height:=h*16;
case RGZoom.ItemIndex of
0: begin Zoom:=1; w:=bm.Width; h:=BM.Height; end;
1: begin Zoom:=4; w:=bm.Width*4; h:=BM.Height*4; end;
2: begin Zoom:=16; w:=bm.Width*16; h:=BM.Height*16; end;
end;
Paintbox1.Refresh;
PaintBox1.canvas.stretchDr
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
BM:=TBitmap.create;
end;
procedure TForm1.FormDestroy(Sender:
begin
bm.free;
end;
procedure TForm1.PaintBox1MouseMove(
Y: Integer);
begin
if (ssLeft in shift) AND (RGTool.itemindex=0)then
DrawPixes(x,y,panel1.color
end;
procedure TForm1.PaintBox1MouseUp(Se
Shift: TShiftState; X, Y: Integer);
begin
XU:=X;YU:=Y;
case RGTool.itemindex of
0: if (XU = XD) and (YU=YD) then DrawPixes(x,y,panel1.color
1: DrawLine(xd,yd,xu,yu,zoom,
2: DrawCircle(xd,yd,xu,yu,zoo
3: DrawBox(xd,yd,xu,yu,zoom, Panel1.color);
4: Flood(xd,yd, Panel1.color);
end;
end;
procedure TForm1.DrawLine(xS,YS,xE,y
begin
paintbox1.Canvas.MoveTo(xS
paintbox1.Canvas.pen.Color
paintbox1.Canvas.pen.Width
paintbox1.Canvas.LineTo(xE
end;
procedure TForm1.DrawCircle(xS,YS,xE
begin
paintbox1.Canvas.MoveTo(xS
paintbox1.Canvas.pen.Color
paintbox1.Canvas.pen.Width
paintbox1.Canvas.brush.Sty
paintbox1.Canvas.Ellipse(x
end;
procedure TForm1.DrawBox(xS,YS,xE,yE
begin
paintbox1.Canvas.MoveTo(xS
paintbox1.Canvas.pen.Color
paintbox1.Canvas.pen.Width
paintbox1.Canvas.brush.Sty
paintbox1.Canvas.Rectangle
end;
procedure TForm1.Flood(xS,YS :integer; Col:TColor);
var c:TColor;
begin
paintbox1.Canvas.brush.Col
c:=paintbox1.Canvas.Pixels
paintbox1.Canvas.FloodFill
end;
end.
(*
==========================
The form:
==========================
*)
object Form1: TForm1
Left = 233
Top = 107
Width = 696
Height = 480
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Shell Dlg 2'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 13
object PaintBox1: TPaintBox
Left = 32
Top = 24
Width = 321
Height = 329
OnMouseDown = PaintBox1MouseDown
OnMouseMove = PaintBox1MouseMove
OnMouseUp = PaintBox1MouseUp
end
object Button1: TButton
Left = 32
Top = 368
Width = 75
Height = 25
Caption = 'Load'
TabOrder = 0
OnClick = Button1Click
end
object Panel1: TPanel
Left = 32
Top = 400
Width = 73
Height = 25
Caption = 'Color'
TabOrder = 1
OnClick = Panel1Click
end
object rgZoom: TRadioGroup
Left = 128
Top = 360
Width = 89
Height = 65
Caption = 'Zoom'
ItemIndex = 0
Items.Strings = (
'1'
'4'
'16')
TabOrder = 2
OnClick = rgZoomClick
end
object RGTool: TRadioGroup
Left = 248
Top = 328
Width = 89
Height = 113
Caption = 'RGTool'
ItemIndex = 0
Items.Strings = (
'Free line'
'Line'
'Circle'
'Rectangle'
'Flodfill')
TabOrder = 3
end
object CD: TColorDialog
Ctl3D = True
Left = 16
Top = 400
end
end
ASKER
You are right, I should paint onto the bmp.
But anyway. The question is on the one hand whether ther is a start up code example around, which I could study and improve
or
to find the optimal way to start, since I do not want to turn back after half work.
What I tried:
The normal drawing on the canvas is so far ok. I can do the basic shapes and a selection rectangle.
But all image editors show a gutter when increasing the zoom. And every pixel is Pixel*zoom big.
I wonder how this gutter work.
I have to translate every move of the mouse in bigpixels (circle: in which bigpixel it is in?).
3rd party tools I ve tried:
There was a bmpeditor component from colin wilson, giving me the right start, but this thing was created for an old delphi version and it doesnt work as far I tested.
IconEdit32 from Neil Rubenkrieg. This could be perfect, but very enhanced to get through the basics.
But anyway. The question is on the one hand whether ther is a start up code example around, which I could study and improve
or
to find the optimal way to start, since I do not want to turn back after half work.
What I tried:
The normal drawing on the canvas is so far ok. I can do the basic shapes and a selection rectangle.
But all image editors show a gutter when increasing the zoom. And every pixel is Pixel*zoom big.
I wonder how this gutter work.
I have to translate every move of the mouse in bigpixels (circle: in which bigpixel it is in?).
3rd party tools I ve tried:
There was a bmpeditor component from colin wilson, giving me the right start, but this thing was created for an old delphi version and it doesnt work as far I tested.
IconEdit32 from Neil Rubenkrieg. This could be perfect, but very enhanced to get through the basics.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
With gutter I mean the horizontal and vertical lines, usually drawn on the "canvas" showing boxes like in a math paper.
But what I do not understand here is the mouse movement.
Ok, it is much easier to draw on the original size bitmap then on the zoomed, and then translate the bitmap into the zoomed paintbox.
But then the mouse will be moved only over the zoomed picture. The user draw where the mouse is, and he want to draw on the large bitmap. I guess this is even harder to tackle?
(Btw. I probably will not be online for a week from later today on)
But what I do not understand here is the mouse movement.
Ok, it is much easier to draw on the original size bitmap then on the zoomed, and then translate the bitmap into the zoomed paintbox.
But then the mouse will be moved only over the zoomed picture. The user draw where the mouse is, and he want to draw on the large bitmap. I guess this is even harder to tackle?
(Btw. I probably will not be online for a week from later today on)
???
if you look at the code in the IconEdit32 from Neil Rubenkrieg you can see that he uses an array for each Icon size that he supports (16x16, 32x32, 48x48), each array has the color of the pixel and other info for that pixel, and when he draws his Icon for editing he DOES NOT stretch Draw the icon bitmap to his display, he for loops through his Array and draws a single rectangle (with a gutter as a black pen of the rectangle draw) for every pixel in the icon, , , .
However, you should NOT use this method for general Bitmap editing, because for an Icon you have fixed small sizes (32x32), and for a bitmap you do not have any fixed sizes, so it would be difficult to have any array with your pixel definitions in it
Maybe if you ask some more I can help you some? ?
but this is not so easy
if you look at the code in the IconEdit32 from Neil Rubenkrieg you can see that he uses an array for each Icon size that he supports (16x16, 32x32, 48x48), each array has the color of the pixel and other info for that pixel, and when he draws his Icon for editing he DOES NOT stretch Draw the icon bitmap to his display, he for loops through his Array and draws a single rectangle (with a gutter as a black pen of the rectangle draw) for every pixel in the icon, , , .
However, you should NOT use this method for general Bitmap editing, because for an Icon you have fixed small sizes (32x32), and for a bitmap you do not have any fixed sizes, so it would be difficult to have any array with your pixel definitions in it
Maybe if you ask some more I can help you some? ?
but this is not so easy
ASKER
but one last question in that.
I want to paste images as well.
I guess i need an new image which freely can be moved with the mouse over the paintbox. Meanwhile my paintboxthing is an object. SO i have to create the image there.
What do you suggest?
I want to paste images as well.
I guess i need an new image which freely can be moved with the mouse over the paintbox. Meanwhile my paintboxthing is an object. SO i have to create the image there.
What do you suggest?
ASKER
..so pasting is fine, but how to get something visible for the user??
I dont see the image!
thanks.
I dont see the image!
thanks.
sorry, but I do not really follow your comments (the description of what you want and what you have). . . You say
"I dont see the image!"
???
if you want to see something (I guess it's a Bitmap that you have pasted an image into? ? ?) then you will need to draw it somewhere. . . . try Stretch Draw that new pasted bitmap onto your Paintbox, OR replace the current bitmap that is drawn on the paintbox with the new pasted one. . . . .
and you say
"I guess i need an new image which freely can be moved with the mouse over the paintbox."
I really do NOT see this a a good thing at all? Moving an Image with mouse? ?
but I can not follow your needs from what you say
"I dont see the image!"
???
if you want to see something (I guess it's a Bitmap that you have pasted an image into? ? ?) then you will need to draw it somewhere. . . . try Stretch Draw that new pasted bitmap onto your Paintbox, OR replace the current bitmap that is drawn on the paintbox with the new pasted one. . . . .
and you say
"I guess i need an new image which freely can be moved with the mouse over the paintbox."
I really do NOT see this a a good thing at all? Moving an Image with mouse? ?
but I can not follow your needs from what you say
ASKER
The idea is to store the pasted image in a timage which I can freely move until I merge it into the bitmap.
However meanwhile I realize that I will draw the pasteimage on the canvas of the paintbox while moving the mouse. On duble click I will merge it into the bitmap.
This is going to work, but also here the paintbox flickers. Do you have an idea how to get rid of this?
Thanks
and
cheers
hh
However meanwhile I realize that I will draw the pasteimage on the canvas of the paintbox while moving the mouse. On duble click I will merge it into the bitmap.
This is going to work, but also here the paintbox flickers. Do you have an idea how to get rid of this?
Thanks
and
cheers
hh
???
it flickers, OK,
as to your question - - - no I do not,
since I do not have any Idea, what so ever, , about what you are doing or the methods you are using to do it,
I am not a mind reader, or a psychic trans-receptor able to read thoughts or computer binary data from a distance ! !
could you answer your question with the limited information that you have given?
Here is a VAST GENERALITY about fickering on screen
if you call several DC drawing operations in sequence (in the same function, line after line) and these DC draws overlap (draw on the same area of the DC or in delhli a Canvas) then you might get "Filcker". flicker happens when your over draw the same area on screen and a screen "Refresh" occures while your lines of core are executed, You get a screen paint of One color, and then a screen refresh and then your code continues and the screen is painted with another color, and the color change will look like a "Flicker", . . . . .
this is very aparent if you do a mouse move paint (drag an image) and do a Two paint (one paint to erase and one paint to draw new image) and your Two paints overlap (almost ALWAYS overlap), I have solved this problem, by creating a Bitmap that will cover the ENTIRE area of the old erase area and the new draw image area, you copy the underlying "Base" bitmap to the new bitmap and then draw your sprite (the moving image) to this new bitmap (in the corrct position) and then do a SINGLE draw onscreen of this new bitmap,
if you do NOT draw on the same areas of the DC twice, you will never get flicker
= = = = = = = = = = = = = = = == = = = = = = = =
I hope that you have succes, and I am glad to help you, but I am not able to follow your last few questions, due to lack of information,
it flickers, OK,
as to your question - - - no I do not,
since I do not have any Idea, what so ever, , about what you are doing or the methods you are using to do it,
I am not a mind reader, or a psychic trans-receptor able to read thoughts or computer binary data from a distance ! !
could you answer your question with the limited information that you have given?
Here is a VAST GENERALITY about fickering on screen
if you call several DC drawing operations in sequence (in the same function, line after line) and these DC draws overlap (draw on the same area of the DC or in delhli a Canvas) then you might get "Filcker". flicker happens when your over draw the same area on screen and a screen "Refresh" occures while your lines of core are executed, You get a screen paint of One color, and then a screen refresh and then your code continues and the screen is painted with another color, and the color change will look like a "Flicker", . . . . .
this is very aparent if you do a mouse move paint (drag an image) and do a Two paint (one paint to erase and one paint to draw new image) and your Two paints overlap (almost ALWAYS overlap), I have solved this problem, by creating a Bitmap that will cover the ENTIRE area of the old erase area and the new draw image area, you copy the underlying "Base" bitmap to the new bitmap and then draw your sprite (the moving image) to this new bitmap (in the corrct position) and then do a SINGLE draw onscreen of this new bitmap,
if you do NOT draw on the same areas of the DC twice, you will never get flicker
= = = = = = = = = = = = = = = == = = = = = = = =
I hope that you have succes, and I am glad to help you, but I am not able to follow your last few questions, due to lack of information,
ASKER
So thanks for your help.
I really have appreciated your comments and help.
It might not be the best way to draw on a bitmap while showing everything on a paintbox but so far it works. I hope I will have no bad awakening in that.
When it comes to the flickering you are right again.
I always do a
showimage (display the bitmap stuff on the canvas of the paintbox) and a drawraster(drawing the lines crosswise) after performing an action.
Of course when drawing a single rectangle this approach is fine.
Now when drawing free hand, meaning every pixel i touch will be colored, the paintbox will permanently be repainted. This is where the flickering starts.
The only idea "I" have about this is, that I need to find a way to control whether the mouse has moved to another pixel when asking for the drawing function. Then i will not ask for repainting so often.
cheers
hh
I really have appreciated your comments and help.
It might not be the best way to draw on a bitmap while showing everything on a paintbox but so far it works. I hope I will have no bad awakening in that.
When it comes to the flickering you are right again.
I always do a
showimage (display the bitmap stuff on the canvas of the paintbox) and a drawraster(drawing the lines crosswise) after performing an action.
Of course when drawing a single rectangle this approach is fine.
Now when drawing free hand, meaning every pixel i touch will be colored, the paintbox will permanently be repainted. This is where the flickering starts.
The only idea "I" have about this is, that I need to find a way to control whether the mouse has moved to another pixel when asking for the drawing function. Then i will not ask for repainting so often.
cheers
hh
I will just take a guess here . . . .
DO NOT CALL for a total repaint for a mouse move sort of draw, you should ONLY PAINT the Rectangle (I guess that it represents a Pixel) or rectangles that the mouse has moved over, , not all the rectangles,
and yes you should NOT paint a rect that has already been painted
I beleive that IconEdit32 from Neil Rubenkrieg , he implements this, doesn't he? Can you not review his code and get some methods from him?
I looked up his code in the imFrameU or imFrame Unit, he uses a TPaintBox named "pbDraw"
here is the paintBox mouse move -
procedure TImageFrame.pbDrawMouseMov e(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
//Translate this mouse-move event into a 'cell-move' event, as
// long as the cell location IS actually changed
VAR cX, cY, Stat : Integer;
begin
CellFmPixel(X, Y, cX, cY);
//Stats is 1 if left button down, 2 if right, 0 if neither
Stat := fGotDown;
//Set 8-bit in Stat if Ctrl key is down
IF ssCtrl IN Shift THEN Stat := Stat OR 8;
//If cell-location has changed...
IF (cX <> fMouseX) OR (cY <> fMouseY) THEN
begin
fMouseX := cX;
fMouseY := cY;
IF Assigned(fECellMove) THEN
fECellMove(Self, fMouseX, fMouseY, Stat);
end;
end;
function TImageFrame.CellFmPixel(X, Y: Integer; VAR cX, cY: Integer):
Boolean;
// Take an X,Y pixel coordinate and put the corresponding
// cell within the drawing area into cX,cY. Return false if
// outside the drawing grid.
begin
IF (X < 0) OR (Y < 0) THEN
begin cX := -1; cY := -1; end
ELSE IF (X >= fCellAll) OR (Y >= fCellAll) THEN
begin cX := -1; cY := -1; end
ELSE
begin
cX := X DIV fCellOne;
cY := Y DIV fCellOne;
end;
Result := ValidCell(cX, cY);
IF NOT Result THEN
begin cX := -1; cY := -1; end;
end;
DO NOT CALL for a total repaint for a mouse move sort of draw, you should ONLY PAINT the Rectangle (I guess that it represents a Pixel) or rectangles that the mouse has moved over, , not all the rectangles,
and yes you should NOT paint a rect that has already been painted
I beleive that IconEdit32 from Neil Rubenkrieg , he implements this, doesn't he? Can you not review his code and get some methods from him?
I looked up his code in the imFrameU or imFrame Unit, he uses a TPaintBox named "pbDraw"
here is the paintBox mouse move -
procedure TImageFrame.pbDrawMouseMov
Shift: TShiftState; X, Y: Integer);
//Translate this mouse-move event into a 'cell-move' event, as
// long as the cell location IS actually changed
VAR cX, cY, Stat : Integer;
begin
CellFmPixel(X, Y, cX, cY);
//Stats is 1 if left button down, 2 if right, 0 if neither
Stat := fGotDown;
//Set 8-bit in Stat if Ctrl key is down
IF ssCtrl IN Shift THEN Stat := Stat OR 8;
//If cell-location has changed...
IF (cX <> fMouseX) OR (cY <> fMouseY) THEN
begin
fMouseX := cX;
fMouseY := cY;
IF Assigned(fECellMove) THEN
fECellMove(Self, fMouseX, fMouseY, Stat);
end;
end;
function TImageFrame.CellFmPixel(X,
Boolean;
// Take an X,Y pixel coordinate and put the corresponding
// cell within the drawing area into cX,cY. Return false if
// outside the drawing grid.
begin
IF (X < 0) OR (Y < 0) THEN
begin cX := -1; cY := -1; end
ELSE IF (X >= fCellAll) OR (Y >= fCellAll) THEN
begin cX := -1; cY := -1; end
ELSE
begin
cX := X DIV fCellOne;
cY := Y DIV fCellOne;
end;
Result := ValidCell(cX, cY);
IF NOT Result THEN
begin cX := -1; cY := -1; end;
end;
I'm not sure that the code above is very clear to understand, here is a prtion of code I have used to do this
the TStart is a Position Record That set on a Mouse Down event
type
TStart = Record
PaintOp, // has the Paint operation, like Draw Line, Draw Rectangle
X, Y, // has center points of cell in pixels on the PaintBox
PosX, PosY: Integer; // has the grid array index positions for the cell, should have called them IndexX. IndexY
end;
// GetGridPos function is called on a mouse event to get the cellGrid index numbers and Position
function GetGridPos(X, Y: Integer): TStart;
begin
if cellRec.Size = 16 then
begin // cell size for 16x16 is 24 pixels
Result.PosX := X div 24;
// PosX and PosY are the array indexes for the cells
// for a 16x16 the array would be Array[0..15, 0..15] of cellColorRecord
Result.PosY := Y div 24;
Result.X := (Result.PosX * 24)+ 12;
// Result.X and Result.Y are the center points if that cell in pixels
// these center points are used for mouse move drawing operations
Result.Y := (Result.PosY * 24)+ 12;
end else
if cellRec.Size = 32 then
begin // cell size for 32x32 is 12 pixels
Result.PosX := X div 12;
Result.PosY := Y div 12;
Result.X := (Result.PosX * 12)+ 6;
Result.Y := (Result.PosY * 12)+ 6;
end else
begin //size 48
Result.PosX := X div 8;
Result.PosY := Y div 8;
Result.X := (Result.PosX * 8)+ 4;
Result.Y := (Result.PosY * 8)+ 4;
end;
end;
the TStart is a Position Record That set on a Mouse Down event
type
TStart = Record
PaintOp, // has the Paint operation, like Draw Line, Draw Rectangle
X, Y, // has center points of cell in pixels on the PaintBox
PosX, PosY: Integer; // has the grid array index positions for the cell, should have called them IndexX. IndexY
end;
// GetGridPos function is called on a mouse event to get the cellGrid index numbers and Position
function GetGridPos(X, Y: Integer): TStart;
begin
if cellRec.Size = 16 then
begin // cell size for 16x16 is 24 pixels
Result.PosX := X div 24;
// PosX and PosY are the array indexes for the cells
// for a 16x16 the array would be Array[0..15, 0..15] of cellColorRecord
Result.PosY := Y div 24;
Result.X := (Result.PosX * 24)+ 12;
// Result.X and Result.Y are the center points if that cell in pixels
// these center points are used for mouse move drawing operations
Result.Y := (Result.PosY * 24)+ 12;
end else
if cellRec.Size = 32 then
begin // cell size for 32x32 is 12 pixels
Result.PosX := X div 12;
Result.PosY := Y div 12;
Result.X := (Result.PosX * 12)+ 6;
Result.Y := (Result.PosY * 12)+ 6;
end else
begin //size 48
Result.PosX := X div 8;
Result.PosY := Y div 8;
Result.X := (Result.PosX * 8)+ 4;
Result.Y := (Result.PosY * 8)+ 4;
end;
end;
ASKER
...
I guess this is comparable to my approach
FUNCTION TIconPaintBox.RasterMouseP oint(x, y: integer): TPoint;
//get mouse to snap to the PIXEL" raster on the Paintbox canvas
BEGIN
IF (x > 0) AND (FZoom > 0) THEN
begin
if (X > (width - FZoom div 2)) then x:= width else
IF (X < (FZoom div 2)) then x:= 0 else
// if (x MOD FZoom) > (FZoom div 2) then
// x := x + x MOD FZoom else
x := x - x MOD FZoom;
END ELSE x := 0;
IF (y > 0) AND (FZoom > 0) THEN
BEGIN
IF (Y > (height - FZoom div 2)) then Y:= height else
IF (Y < (FZoom div 2)) then Y:= 0 else
// if (Y MOD FZoom) > (FZoom div 2) then
// y := y + y MOD FZoom else
y := y - y MOD FZoom;
END ELSE y := 0;
Result := Point(x, y);
END;
But I cant say for sure
I guess this is comparable to my approach
FUNCTION TIconPaintBox.RasterMouseP
//get mouse to snap to the PIXEL" raster on the Paintbox canvas
BEGIN
IF (x > 0) AND (FZoom > 0) THEN
begin
if (X > (width - FZoom div 2)) then x:= width else
IF (X < (FZoom div 2)) then x:= 0 else
// if (x MOD FZoom) > (FZoom div 2) then
// x := x + x MOD FZoom else
x := x - x MOD FZoom;
END ELSE x := 0;
IF (y > 0) AND (FZoom > 0) THEN
BEGIN
IF (Y > (height - FZoom div 2)) then Y:= height else
IF (Y < (FZoom div 2)) then Y:= 0 else
// if (Y MOD FZoom) > (FZoom div 2) then
// y := y + y MOD FZoom else
y := y - y MOD FZoom;
END ELSE y := 0;
Result := Point(x, y);
END;
But I cant say for sure
I wonder what I am suppose to look at your code above and tell you about? as I said this graphic editing can get very complex, but I wonder why why you draw on the Paintbox, as in your DrawBox
paintbox1.Canvas.Rectangle
shouldn't you be drawing on the bitmap?, any draw on the paintbox is lost, not remembered.