Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Linie zeichnen mit Vorschau (wie in Paint)


Wolle92 - So 25.05.08 13:36
Titel: Linie zeichnen mit Vorschau (wie in Paint)
Hallo,

ich bin grad etwas am rumexperimentieren, mit nem Zeichenprogramm...
Einfach wild draufloszeichnen geht schon, gibts auch keine Probleme...
Allerdings gibts nen Problem, wenn ich ne Linie zeichnen lassen will, wie bei Paint, also Maus gedrückt halten, ziehen, Maus loslassen...
Und zwar hab ich dann ja immer ne Vorschau wolang meine Linie geht...
Diese Vorschau übermale ich im MouseMove immer wieder mit einer weißen Linie, das Problem ist jetzt, das bereits existierende Linien dadurch dann auch wieder weiß werden an einigen Stellen...

Wie kann ich das Problem lösen? Hat jemand ne Idee?


nagel - So 25.05.08 14:06

Du musst die alte Linie mit Pen.Mode := pmNotXor übermalen.


Wolle92 - So 25.05.08 14:28

dann kommt da irgendwas komisches bei raus...
da wird nicht alles gelöscht, wenn der hintergrund weiß ist...


nagel - So 25.05.08 14:41

Komisch, dachte das geht so.
Hast du die Stiftfarbe geändert?


Wolle92 - So 25.05.08 14:57

äh, ja...
Hab das aber grad mal ohne ändern der Farbe gemacht, bringt auch nix...


Narses - So 25.05.08 15:29

Moin!

user profile iconnagel hat folgendes geschrieben:
Du musst die alte Linie mit Pen.Mode := pmNotXor übermalen.
Du musst beide Linien mit XOR malen, sonst hebt sich das ja nicht wieder auf. :idea: ;)

cu
Narses


Wolle92 - So 25.05.08 16:01

jetzt wird aber noch der schnittpunkt von zwei linien weiß...

einfach nochmal ne schwarze Linie mit pmCopy drüber?


Narses - So 25.05.08 16:17

Moin!

user profile iconWolle92 hat folgendes geschrieben:
jetzt wird aber noch der schnittpunkt von zwei linien weiß...
Was willst du denn machen? Eine Vorschau, so wie die Linie nachher auch gezeichnet aussehen würde? Oder nur eine Visualisierung, wo es lang geht... :nixweiss:

Im ersten Fall musst du halt das Bitmap vor dem Zeichnen kopieren und dann dazu verwenden, deine Vorschau-Linie wieder zu entfernen (betreffenden Bereich zurück kopieren - oder einfach das ganze Bitmap wiederherstellen).

cu
Narses


Hidden - So 25.05.08 16:36

Ich würde die bisherigen Zeichnungen auf eine Bitmap zeichnen, die du im OnPaint auf deine PaintBox(du verwendest doch TPaintBox?) zeichnest. Deine Vorschau zeichnest du dann einfach außerhalb des OnPaint auf die PaintBox, ist also nichts permanentes.


Wolle92 - So 25.05.08 17:49

Nein, ich benutze TImage, steht auch im Titel...

Ich will einfach sowas machen wie bei Paint...
Also MouseDown, Linie ziehen, MouseUp -> Linie...
Und wenn sich dann zwei Linien schneiden, auch während des Ziehens, dann sollen die trotzdem durchgezogen werden und sich nicht stören...


ub60 - So 25.05.08 17:54

So geht es:

MouseMove:
-Zeichnen der Vorschau: pmNotXor
-Löschen: pmNotXor
MouseUp:
-abschließendes Zeichnen: pmCopy

ub60


Wolle92 - So 25.05.08 19:50

so hab ichs auch gemacht...


Hidden - So 25.05.08 20:44

Hi,

die PaintBox ist ja sowieso besser und kannst du a leicht austauschen. Da du so eine "reserveBitmap" sparst, empfehle ich den Umstieg.

mfG,


Wolle92 - So 25.05.08 21:12

Irgendwie gefällt mir die Lösung bei farbig gefüllten Rechtecken nicht, da alles was dahinter ist immer weiß, schwarz, gelb, rot oder sonstwie wird...

Vielleicht nehm ich doch die PaintBox?


Hidden - So 25.05.08 21:28

Hi,

IMHO hast du drei Möglichkeiten:
mfG,


Wolle92 - Mo 26.05.08 13:15

Irgendwie mag mich die Bitmap aber nicht... Ich kann egal was ich mache immer nur direkt aufs Image / auf die PaintBox zeichen, mit der Bitmap geht gar nix...


Hidden - Mo 26.05.08 17:05

Hi,

Deine Frage war nicht ganz präzise. Insofern umreiße ich einfach mal, wie es klappen müsste:

Wenn das so nicht klappt, lass mal ein bisschen Code sehen.

mfG,


Wolle92 - Mo 26.05.08 17:18

jetzt versteh ich gar nix mehr...
Wie kann ich denn dann das MouseMove und so mit der Bitmap verbinden?


Hidden - Mo 26.05.08 17:22

Hi,

Du musst das MouseMove der PaintBox verwenden.

Eine PaintBox ist eine Zeichenfläche, die nach jedem neuen Zeichnen wieder grau ist: der Inhalt wird nicht gespeichert.
Die PaintBox hat ein OnPaint-Ereignis, in dem du das Bild auf ihr erneuern musst. In diesem zeichnest du nun die Bitmap auf die PaintBox.

Den nichtpermanenten Strich zeichnest du im OnMouseMove direkt auf die PaintBox, er wird also beim nächsten Repaint weg sein. Am Ende, wenn die Maus abgesetzt wird, zeichnest du den Strich dann auf die Bitmap und machst ihn so permanent.

Noch ein PaintBox.Repaint und alles ist zu sehen.

mfG,


Wolle92 - Mo 26.05.08 17:33

das flimmert aber wie sonstwas...

und trotzdem ich eigentlich beim onmouseup die linie dann direkt in die Bitmap zeichne wirs die nicht gespeichert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  mouseDown := true;
  startX := X;
  startY := Y;
  oldX := X;
  oldY := Y;
  Bitmap1.Canvas.MoveTo(X, Y);
end;

procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  StatusBar1.Panels[1].Text := IntToStr(X) + ', ' + IntToStr(Y);
  PaintBox1.Repaint;
  if not mouseDown then Exit;
  case drawMode of
    dmFree:
    begin
      Bitmap1.Canvas.LineTo(X, Y);
    end;
    dmLine:
    begin
      with PaintBox1.Canvas do
      begin
        MoveTo(startX, startY);
        LineTo(X, Y);
        oldX := X;
        oldY := Y;
      end;
    end;
    dmBorderRect:
    begin
      with PaintBox1.Canvas do
      begin
        Rectangle(startX, startY, X, Y);
        oldX := X;
        oldY := Y;
      end;
    end;
  end;
end;

procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  mouseDown := false;
  case drawMode of
    dmLine: Bitmap1.Canvas.LineTo(startX, startY);
    dmBorderRect: Bitmap1.Canvas.Rectangle(startX, startY, oldX, oldY);
  end;
end;


Hidden - Mo 26.05.08 17:57

user profile iconWolle92 hat folgendes geschrieben:
das flimmert aber wie sonstwas...

und trotzdem ich eigentlich beim onmouseup die linie dann direkt in die Bitmap zeichne wirs die nicht gespeichert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  mouseDown := true;
  startX := X;
  startY := Y;
  oldX := X;
  oldY := Y;
  Bitmap1.Canvas.MoveTo(X, Y);  //das würde ich später machen..
end;

procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  StatusBar1.Panels[1].Text := IntToStr(X) + ', ' + IntToStr(Y);
  PaintBox1.Repaint;  //wozu repaint, wenn du nichst verändert hast?
  if not mouseDown then Exit;
  PaintBox1.Repaint;  //hier muss es hin, dann flimmerts nicht. Alternativ gibts Doublebuffered
  case drawMode of
    dmFree: Bitmap1.Canvas.LineTo(X, Y);
    dmLine:
    begin
      PaintBox1.Canvas.MoveTo(startX, startY);
      PaintBox1.Canvas.LineTo(X, Y);
      oldX := X;
      oldY := Y;
    end;
    dmBorderRect:
    begin
      PaintBox1.Canvas.Rectangle(startX, startY, X, Y);
      oldX := X;
      oldY := Y;
    end;
  end;
end;

procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  mouseDown := false;
  case drawMode of
    dmLine:
    begin
      Bitmap1.Canvas.MoveTo(oldX, oldY);
      Bitmap1.Canvas.LineTo(startX, startY);
    end;
    dmBorderRect: Bitmap1.Canvas.Rectangle(startX, startY, oldX, oldY);
  end;
  PaintBox1.Repaint;  //sonst siehst du nichts
end;


Hast du ins OnPaint reingeschrieben "PaintBox1.Canvas.Stretchdraw(PaintBox1.ClientRect, Bitmap1)"?


G-S - Mo 26.05.08 18:10

Wie wärs, wenn du 2 TIMages verwendest?

Auf der unteren ist das eigentlich Bild, auf der oberen die Linie und diese wird erst bei OnMouseClick auf die untere übertragen, somit brauchst du das obere Image-Objekt nur zu "leeren".


Wolle92 - Mo 26.05.08 18:25

daran hatte ich auch est gedacht, jetzt bin ich aber auf die PaintBox umgestiegen...

Ja, ich habs ins OnPaint reingepackt


Hidden - Mo 26.05.08 19:19

user profile iconG-S hat folgendes geschrieben:
Wie wärs, wenn du 2 TIMages verwendest?

Auf der unteren ist das eigentlich Bild, auf der oberen die Linie und diese wird erst bei OnMouseClick auf die untere übertragen, somit brauchst du das obere Image-Objekt nur zu "leeren".


Hi,

Das ist genau der falsche Ansatz. Man braucht stets maximal eine Bildausgabe.

mfG,